Google Cloud の 拒否ポリシー(Deny ポリシー)を利用して、特定の操作を制限する

ガバナンスの向上として、Google Cloud でプロジェクト全体として禁止したい操作がある場合、拒否ポリシー(Deny ポリシー)を活用すると良いでしょう。
本記事では、 Deny ポリシーの概要から、実際に特定の権限を拒否する方法について紹介いたします。

Deny ポリシーとは

Identity and Access Management(IAM)拒否ポリシーを使用すると、Google Cloud リソースへのアクセスにガードレールを設定できます。
拒否ポリシーを使用すると、付与されるロールに関係なく、特定のプリンシパルが特定の権限を使用できないようにする拒否ルールを定義できます。

引用: 拒否ポリシー

Deny ポリシー は、IAM に付与されているロールに関係なく、特定のプリンシパルに対して特定の操作を禁止することができます。

特徴として、誰が何を実行できるか(許可) ではなく、誰が何を実行できないか(拒否) を明示的に指定できる点が IAM ポリシーと大きく異なる点です。
また、許可よりも拒否のほうが優先されるため、IAM ポリシーで許可されているような操作であっても、Deny ポリシーによって拒否されている場合、その操作は実行できません。

ユースケース

一般的なケースでは、プロジェクト関連の操作を禁止する場合などに利用できます。
また、特定のサービスにおいて、コンプライアンス要件を遵守させる場合や重要なデータの保護にも利用できます。

プロジェクトの削除や請求先の連携解除、重要なデータが保存された特定バケットの削除といった、誤って実行された場合の影響が大きく、拒否したい操作が明確な場合は Deny ポリシーが適しています。

注意点として、Deny ポリシーはコンソール上で表示や変更などの操作が一切できず、CLI または IAM v2 REST API でのみ操作が可能です。そのため、一時的に許可する必要がある権限を Deny ポリシーで指定すると、Deny ポリシーの修正頻度が高くなり、運用コストが高くなるケースもあります。

また、Deny ポリシーは下位の階層に継承されるため、用途が異なるフォルダやプロジェクトが存在する場合、組織に Deny ポリシーを付与すると、意図していないプロジェクトで特定の操作を禁止してしまう可能性があります。

そのため、常に拒否したい操作がある場合や広範に操作を禁止したい場合に利用することが適していると思います。

Deny ポリシーの操作に必要な権限

Deny ポリシーを操作する権限として、事前定義ロールが2つ存在します。

Deny ポリシーの表示には、拒否審査担当者 ( roles/iam.denyReviewer ) ロールが必要になります。
Deny ポリシーの表示、作成、更新、削除には、拒否管理者 ( roles/iam.denyAdmin ) ロールが必要になります。

Deny ポリシーをアタッチできる対象

Deny ポリシーは接続ポイントとして、組織 or フォルダ or プロジェクトにアタッチすることができます。
また、継承される特徴があるため、組織で Deny ポリシーを設定することで、下の階層にあるフォルダやプロジェクトに対して特定の操作を拒否することができます。

接続ポイントを指定する際は、それぞれ以下のような形式になります。

接続ポイント 形式
組織 cloudresourcemanager.googleapis.com/organizations/ORG_ID
フォルダ cloudresourcemanager.googleapis.com/folders/FOLDER_ID
プロジェクト cloudresourcemanager.googleapis.com/projects/PROJECT_ID

Deny ポリシーで拒否できるプリンシパル

“誰が何を実行できないか”の、誰が にあたる箇所です。

Deny ポリシーでは、プリンシパルを IAM v2 API で指定する必要があります。
ユーザーやサービスアカウントを指定する場合、IAM v2 API では以下のような形式になります。

Principal type Identifier Example
User principal://goog/subject/USER_EMAIL_ADDRESS principal://goog/subject/my-user@example.com
Service account principal://iam.googleapis.com/projects/-/serviceAccounts/SA_EMAIL_ADDRESS principal://iam.googleapis.com/projects/-/serviceAccounts/my-service-account@my-project.iam.gserviceaccount.com

その他にも全てのプリンシパルといった指定も可能です。
指定可能なプリンシパルは以下ドキュメントを参照してください。
IAM v2 API

操作を拒否できる権限

“誰が何を実行できないか”の、何を にあたる箇所です。

Deny ポリシーでは、compute.googleapis.com/〇〇 のような記載で拒否したい権限を指定することができます。

注意点として、Deny ポリシーで全ての権限はサポートしていないため、どのような権限を拒否できるかは以下のドキュメントを参照してください。
Permissions supported in deny policies

Deny ポリシーの設定方法 (CLI)

Deny ポリシーの設定を記載した json ファイルを用意し、gcloud コマンドで組織 or フォルダ or プロジェクトに設定することで適用されます。

設定の流れ
1. 拒否する権限を記入したjsonファイルを準備
2. gcloud iam policies create を実行して、接続ポイントにアタッチ
3. gcloud iam policies list or get で接続ポイントにアタッチされていることを確認

 

jsonファイル作成

jsonファイルに記載すべき項目は以下の表の通りで、どのプリンシパルに対して何の権限を拒否するのかを記載します。

項目 備考
displayName example-deny-policy 設定するポリシー名
deniedPrincipals principalSet://goog/public:all 操作権限を拒否するプリンシパルのリスト
deniedPermissions “cloudresourcemanager.googleapis.com/projects.delete”,
”cloudresourcemanager.googleapis.com/projects.deleteBillingAssignment”
deniedPrincipals に記載したプリンシパルに対して、操作を拒否する権限のリスト
exceptionPrincipals (省略可) deniedPrincipals で指定されていても例外的に権限が利用できるプリンシパルのリスト
denialCondition (省略可) プリンシパルが権限を利用できなくなる条件
{
    "displayName": “example-deny-policy",
    "rules": [
        {
            "denyRule": {
                "deniedPrincipals": [
                    "principalSet://goog/public:all" 
                ],
                "deniedPermissions": [
                    "cloudresourcemanager.googleapis.com/projects.delete",
                    "cloudresourcemanager.googleapis.com/projects.deleteBillingAssignment"
                ]
            }
        }
    ]
}

補足: 上記jsonファイルは、プロジェクト内の全てのプリンシパル( principalSet://goog/public:all )に対して、プロジェクト削除 ( projects.delete ) 及び請求先アカウントの連携解除 ( projects.deleteBillingAssignment ) の操作を拒否する内容となります。

Deny ポリシーの設定手順

作業結果がわかりやすいように、バケットの削除操作( storage.googleapis.com/buckets.delete ) の拒否を例として検証してみます。

また、今回は CloudShell から gcloud コマンドで作成する手順を記載いたします。

もしTerraform で作成したい場合は、以下ドキュメントにサンプルがありますのでご参照ください。
拒否ポリシーを作成する

事前準備

Cloud Storage にテスト用のバケットを作成します。
現時点ではバケットの削除ができる状態です。

Deny ポリシー用のjsonファイルを準備

バケットの削除ができないような Deny ポリシー用のjsonファイルを準備します。

項目 設定値 備考
ファイル名 test-deny-policy-gcs.json
displayName test-deny-policy-gcs わかりやすいようにファイルと同じ名前を指定
deniedPrincipals principalSet://goog/public:all すべてのプリンシパルを指定
deniedPermissions storage.googleapis.com/buckets.delete Permissions supported in deny policies で権限を検索して指定
exceptionPrincipals (省略可) なし
denialCondition (省略可) なし
{
    "displayName": "test-deny-policy-gcs",
    "rules": [
        {
            "denyRule": {
                "deniedPrincipals": [
                    "principalSet://goog/public:all"
                ],
                "deniedPermissions": [
                    "storage.googleapis.com/buckets.delete"
                ]
            }
        }
    ]
}

今回は上記のjsonファイルを Cloud Shell 上の任意のディレクトリに配置します。

Deny ポリシーを作成

コマンドが長くなってしまうので先にシェル変数を設定します。
また、今回は接続ポイントとしてプロジェクト(例: my-project-id)を指定します。

シェル変数

ATTACHMENT_POINT="cloudresourcemanager.googleapis.com/projects/my-project-id"
POLICY_ID="test-deny-policy-gcs"
POLICY_FILE="test-deny-policy-gcs.json"

その後、設定した変数を使うようにして Deny ポリシーの作成を実施します。
Deny ポリシー作成コマンド

gcloud iam policies create $POLICY_ID --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --policy-file=$POLICY_FILE

実行結果

問題なくコマンドが成功すれば、以下のような実行結果になります。

$ gcloud iam policies create $POLICY_ID --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --policy-file=$POLICY_FILE
Create in progress for denyPolicy [policies/cloudresourcemanager.googleapis.com%2Fprojects%XXXXX/denypolicies/test-deny-policy-gcs/operations/XXXXX].

Deny ポリシーを表示

Deny ポリシーが作成されたか確認してみます。
今回はシェル変数を設定したため、以下コマンドで Deny ポリシーの一覧表示及び詳細表示を行うことができます。

Deny ポリシーの一覧表示

gcloud iam policies list --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --format=json

Deny ポリシーの詳細表示

gcloud iam policies get $POLICY_ID --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --format=json

実行結果

Deny ポリシーの一覧表示

$ gcloud iam policies list --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --format=json
{
  "policies": [
    {
      "createTime": "2024-07-26T11:52:25.523087Z",
      "displayName": "test-deny-policy-gcs",
      "kind": "DenyPolicy",
      "name": "policies/cloudresourcemanager.googleapis.com%2Fprojects%XXXXX/denypolicies/test-deny-policy-gcs",
      "uid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
      "updateTime": "2024-07-26T11:52:25.523087Z"
    }
  ]
}

Deny ポリシーの詳細表示

$ gcloud iam policies get $POLICY_ID --attachment-point=$ATTACHMENT_POINT --kind=denypolicies --format=json
{
  "createTime": "2024-07-26T11:52:25.523087Z",
  "displayName": "test-deny-policy-gcs",
  "etag": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "kind": "DenyPolicy",
  "name": "policies/cloudresourcemanager.googleapis.com%2Fprojects%XXXXX/denypolicies/test-deny-policy-gcs",
  "rules": [
    {
      "denyRule": {
        "deniedPermissions": [
          "storage.googleapis.com/buckets.delete"
        ],
        "deniedPrincipals": [
          "principalSet://goog/public:all"
        ]
      }
    }
  ],
  "uid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "updateTime": "2024-07-26T11:52:25.523087Z"
}

設定後のコンソール表示

ここまでの操作で、 Cloud Storage のバケット削除を禁止する設定がプロジェクトにアタッチされました。
Deny ポリシー 設定後、コンソール上の表示では以下のように削除操作がグレーアウトされます。

カーソルを”削除”に合わせると、権限不足によって操作ができないと表示されます。

エラーメッセージ

選択したバケットを削除するには、プロジェクトのオーナーに「storage.buckets.delete」権限を付与するよう依頼してください。

注意点として、必ずしもグレーアウトされるわけではなく、権限の種類によってはグレーアウトされず、実際に実行してみるとエラーが出て操作が拒否されるケースもあります。

感想

Deny ポリシー によって、プロジェクト管理や環境の制御をより行いやすくなりました。
特にこれまでの IAM の許可ベースとは異なり、誰が何を実行できないか(拒否) を明示的に指定できるため、ガバナンスの向上として非常に有用だと思います。

ただし、Deny ポリシーはGUIで操作できず、設定した際の継承範囲や影響する既存リソースの表示もされないため、
環境の管理として Deny ポリシーをメインの手段とするのは避けたほうが良いです。

そのため、従来通り基本的には IAM ポリシーで環境を制御しつつ、頻繁に変更する可能性がない権限のみ Deny ポリシーで制御する方針が良いと思います。