🎉

Google CloudのIAMの拒否ポリシーがGAになったので試してみる

2022/11/01に公開

こんにちは。クラウドエースでバックエンドエンジニアをしている吉崎です。

2022/10/25 にIAM の拒否ポリシーが GA(General Availability)になりました。
https://cloud.google.com/iam/docs/release-notes
2022/3/3にプレビュー版がリリースされました。

この機能は、名前の通り、プリンシパル(主体)がある権限を使うのを拒否する機能です。

本稿では、以下の内容に焦点を当てて拒否ポリシーを説明します。

  • 拒否ポリシーと許可ポリシーの関係
  • 継承
  • 使用の注意点

前置き

IAM(Identity and Access Management)

主体と権限を管理する機能です。
「主体」、「アクション」、「対象」の 3 つを用いて権限を管理します。

例えば以下のポリシーを作成すると、yoshizaki@hogehoge.com が Compute Engine インスタンスを作成出来るようになります。

主体 アクション 対象
yoshizaki@hogehoge.com 作成する Compute Engine インスタンス

※ポリシーとは、「主体」、「アクション」、「対象」を定義するバインディングをまとめた単位です。ポリシーの集合によって権限を管理します。以下、許可ポリシーの例(公式より引用)です。

{
  "bindings": [
    {
      "role": "roles/storage.objectAdmin",
       "members": [
         "user:ali@example.com",
         "serviceAccount:my-other-app@appspot.gserviceaccount.com",
         "group:admins@example.com",
         "domain:google.com"
       ]
    },
    {
      "role": "roles/storage.objectViewer",
      "members": [
        "user:maria@example.com"
      ]
    }
  ]
}

許可ポリシーの継承

ポリシーは継承されます。
例えば、組織ノードにおいて A という主体に compute.instanceAdmin.v1 というロールを付与した場合、その配下のフォルダとプロジェクトにおいて A に compute.instanceAdmin.v1 が継承されます。
つまり、A は folder1 や project1 において compute.instanceAdmin.v1 のロールを使った操作が実行出来ます。

同様に、folder1 に対して A に compute.instanceAdmin.v1 を付与した場合、その配下の project1 と project2 に継承されます。

このとき、folder1 において A に compute.viewer というより弱いロールを付与したとしても、A が使用出来る権限は compute.instanceAdmin.v1 という強いロールのものであることに注意が必要です。
※よく「権限は和集合となる」と説明されます。弱い権限で縛ることは出来ません。

image

拒否ポリシーと許可ポリシーの関係

説明

拒否ポリシーは、許可ポリシーによって付与された権限に関わらず、ある主体がある権限を使うのを拒否する機能です。
例えば、A に compute.instanceAdmin.v1 が付与されているときに、A に compute.instances.create
の拒否ポリシーを設定すると、A が実行出来る権限は compute.instanceAdmin.v1 - compute.instances.create となります。
つまり、インスタンスの作成が出来ない状態となります。

やってみる

プロジェクトレベルで

下図の通り、project1 において A の compute.instances.create を拒否します。
拒否ポリシーの作成方法は公式ドキュメントを参照ください。

image

gcloud iam policies create mydenypolicy \
    --attachment-point=cloudresourcemanager.googleapis.com/projects/project1-367013 \
    --kind=denypolicies \
    --policy-file=dp.json
Create in progress for denyPolicy [policies/cloudresourcemanager.googleapis.com%2Fprojects%2F822369982041/denypolicies/mydenypolicy/operations/85b59c542e75ff01].
dp.json
{
  "displayName": "My deny policy.",
  "rules": [
    {
      "denyRule": {
        "deniedPrincipals": [
          "principal://goog/subject/[A]"
        ],
        "deniedPermissions": [
          "compute.googleapis.com/instances.create"
        ]
      }
    }
  ]
}

※実際のアカウント名は伏せ、仮に [A] としています。

作成されたことを確認するため、list コマンドを実行します。

$ gcloud iam policies list \
    --attachment-point cloudresourcemanager.googleapis.com/projects/project1-367013 \
    --kind=denypolicies
policies:
- createTime: '2022-10-30T06:44:43.069535Z'
  displayName: My deny policy.
  kind: DenyPolicy
  name: policies/cloudresourcemanager.googleapis.com%2Fprojects%2F822369982041/denypolicies/mydenypolicy
  uid: dc867e79-16e0-3608-48d6-e31b29e2e1e9
  updateTime: '2022-10-30T06:44:43.069535Z'

確かに拒否ポリシー(kind: DenyPolicy)が作成されていることが確認出来ます。

次に、A のアカウントでインスタンス作成を試みます。

A@cloudshell:~ (project1-367013)$ gcloud compute instances create instance-1 --project=project1-367013 (以下略)
ERROR: (gcloud.compute.instances.create) Could not fetch resource:
 - Required 'compute.instances.create' permission for 'projects/project1-367013/zones/asia-northeast1-b/instances/instance-1'

エラー文の下記の部分から分かる通り、拒否した compute.instances.create 権限がないためにインスタンス作成に失敗していることが分かります。

Required 'compute.instances.create' permission

続いて、project1 ではなく folder1 単位で拒否ポリシーを適用した場合に、どう継承されるか確認します。

いったん拒否ポリシーを削除します。

$ gcloud iam policies delete mydenypolicy \
    --attachment-point=cloudresourcemanager.googleapis.com/projects/project1-367013 \
    --kind=denypolicies
Delete in progress for denyPolicy [policies/cloudresourcemanager.googleapis.com%2Fprojects%2F822369982041/denypolicies/mydenypolicy/operations/8c9466202e75ff01].

フォルダレベルで

folder1 に対し先ほどの拒否ポリシーを適用します。

image

フォルダレベルで拒否ポリシーを作成するときは、--attachment-point を下記のように修正すれば OK です。

$ gcloud iam policies create mydenypolicy \
    --attachment-point=cloudresourcemanager.googleapis.com/folders/286455864516 \
    --kind=denypolicies --policy-file=dp.json
Create in progress for denyPolicy [policies/cloudresourcemanager.googleapis.com%2Ffolders%2F286455864516/denypolicies/mydenypolicy/operations/8893a8dfae75ff01].

詳細は割愛しますが、project1 および project2 で A によるインスタンス作成を試みましたが、同じエラーにつきインスタンス作成が失敗しました。

このことから、拒否ポリシーは継承されることが分かります。

また、ポリシーの評価に記載されていますが、許可ポリシーより拒否ポリシーが先に評価されます。
このため、許可ポリシーを設定していようが、それが拒否ポリシーによって否定されていると、その権限は実行出来ません。

使用上の注意点

  • Cloud Console から確認出来ない
    現在のところ、拒否ポリシーは Cloud Console 上からは確認出来ません。
    このため、拒否ポリシーを設定した人以外は、「なぜ権限が不足しているのか」が分からなくなる可能性があります。

  • 権限管理が複雑になる
    許可だけのときより複雑になるのは自明です。
    上位ノードでリソースに対する制限をかけたい場合、組織のポリシーも検討しましょう。

  • プロジェクト、フォルダー、組織ごとに 5 つまでしか適用出来ない
    あまり多くの拒否ポリシーを設定することは想像し難いですが、拒否ポリシーはそれぞれのノードごとに 5 つしか設定出来ません。
    拒否ポリシー内の拒否ルールを複数設定することで多くの権限を拒否するポリシーを作成出来ますが、ポリシーの上限は多くないため、組織レベルで拒否ポリシーを多く設定したい場合は拒否ルールにまとめて記載しましょう。

おわりに

AWS では実現可能だった明示的な拒否が Google Cloud でも GA となりました。
これまで Google Cloud のトレーニングでは「拒否は出来ません」という説明でしたが、これからは「拒否することも出来ます」という説明に変わるのが個人的な大きな影響でした。
とはいえ、権限管理の複雑化に繋がる機能ですので、慎重に使用しましょう。

Discussion