リソースベースのアクセスの構成

このトピックでは、Identity and Access Management(IAM)ポリシーで条件付きロール バインディングを使用して、特定のリソースへのアクセスを管理する方法について説明します。条件式でリソース属性を使用すると、リソース名、リソースタイプ、Google Cloud サービスに基づいてロール バインディングのサブスコープを付与できます。

始める前に

  • IAM の条件付きロール バインディングの基本については、IAM Conditions の概要をご覧ください。
  • 条件式で使用できるリソース属性を確認してください。
  • リソース名属性では、次の Google Cloud サービスへのアクセスを制御できます。
    • Cloud Spanner
    • Cloud Storage
    • Compute Engine
    • Identity-Aware Proxy
    • Cloud Key Management Service
    • Resource Manager(リソースタイプとリソース サービスのみ)
    • Secret Manager

リソース名の接頭辞に基づいてリソースのグループにアクセス権を付与する

条件付きロール バインディングを使用すると、リソース名が接頭辞と一致するリソースのメンバーに、名前が特定の文字列で始まる Compute Engine 仮想マシン(VM)インスタンスなどのアクセスを付与できます。通常、リソース名接頭辞は、特定の機能向けのリソースや特定のプロパティを持つリソースをグループ化するために使用します。

次に例を示します。ソフトウェア企業の ExampleCo は、機密性の高いヘルスケア データを処理する特定の VM インスタンスでワークロードを実行しています。その他の機密性の低いワークロードは同じプロジェクトで実行する必要があり、ExampleCo はデベロッパーが機密データを処理する VM インスタンスへのアクセスを制限しなければなりません。この目的のために、機密性の高いデータが含まれる VM インスタンスの名前には sensitiveAccess 接頭辞が付き、他の VM インスタンスの名前には devAccess 接頭辞が付きます。次に、条件付きロール バインディングを使用して、デベロッパーが通常の devAccess VM インスタンスで生産性を維持できるようにしますが、sensitiveAccess VM インスタンスへのアクセスは付与しません。

resource.name 条件属性のみを使用してアクセスを管理できますが、resource.type 属性と resource.service 属性も使用するのが一般的です。このような追加属性を使用すると、類似した名前の異なるリソースタイプへのアクセスに条件が影響する可能性が低くなります。このセクションの例では、resource.nameresource.type 属性の両方を使用してアクセスを制御します。

プロジェクト内の Compute Engine ディスクとインスタンスに名前接頭辞を使用してアクセスを許可するには、次を行います。

Console

  1. Cloud Console で [IAM] ページに移動します。

    [IAM] ページに移動

  2. リストから目的のメンバーを見つけて、 ボタンをクリックします。

  3. [権限の編集] パネルで、条件を構成するロールを見つけます。次に [条件] の下にある [条件を追加] をクリックします。

  4. [条件の編集] パネルで、条件のタイトルとオプションの説明を入力します。

  5. 条件ビルダーまたは条件エディタを使用して条件式を追加できます。条件ビルダーには、目的の条件タイプ、演算子、その他の式に関する適用可能な詳細を選択するためのインタラクティブなインターフェースが用意されています。条件エディタには、CEL 構文を使用して手動で式を入力するためのテキストベースのインターフェースが用意されています。

    条件ビルダー:

    1. [追加] プルダウンをクリックして、[グループ化された条件] をクリックします。
    2. [条件タイプ] プルダウンから、[リソース] > [タイプ] の順に選択します。
    3. [演算子] プルダウンから、[] を選択します。
    4. [リソースタイプ] プルダウンから、[compute.googleapis.com/Disk] を選択します。
    5. 先ほど入力した条件のすぐ下の最初の [追加] ボタンをクリックして、式に別の句を追加します。
    6. [条件タイプ] プルダウンから、[リソース] > [名前] の順に選択します。
    7. [演算子] プルダウンから、[で始まる] を選択します。
    8. [] フィールドに、devAccess で名前が始まるディスクの projects/project-123/zones/us-central1-a/disks/devAccess などの適切な形式でリソース名を入力します。
    9. 各条件タイプの左側で、[] をクリックして、両方の句が true になるようにします。
    10. [保存] ボタンのすぐ上の [追加] ボタンをクリックして、別の条件のグループを追加します。
    11. [条件タイプ] プルダウンから、[リソース] > [タイプ] の順に選択します。
    12. [演算子] プルダウンから、[] を選択します。
    13. [リソースタイプ] プルダウンから、[compute.googleapis.com/Instance] を選択します。
    14. 先ほど入力した条件のすぐ下の最初の [追加] ボタンをクリックして、式に別の句を追加します。
    15. [条件タイプ] プルダウンから、[リソース] > [名前] の順に選択します。
    16. [演算子] プルダウンから、[で始まる] を選択します。
    17. [] フィールドに、devAccess で名前が始まるインスタンスの projects/project-123/zones/us-central1-a/instances/devAccess などの適切な形式でリソース名を入力します。
    18. 各条件タイプの左側で、[] をクリックして、両方の句が true になるようにします。
    19. [保存] ボタンのすぐ上の [追加] ボタンをクリックして、3 番目の条件のグループを追加します。
    20. この条件が他のリソースに影響しないようにするには、次の句も追加します。[条件タイプ] プルダウンから、[リソース] > [タイプ] の順に選択します。
    21. [演算子] プルダウンから、[ではありません] を選択します。
    22. [リソースタイプ] プルダウンから、[compute.googleapis.com/Disk] を選択します。
    23. 先ほど入力した条件のすぐ下の最初の [追加] ボタンをクリックして、式に別の句を追加します。
    24. [条件タイプ] プルダウンから、[リソース] > [タイプ] の順に選択します。
    25. [演算子] プルダウンから、[ではありません] を選択します。
    26. [リソースタイプ] プルダウンから、[compute.googleapis.com/Instance] を選択します。
    27. 各条件タイプの左側で、[] をクリックして、両方の句が true になるようにします。
    28. 終了すると、条件ビルダーは次のようになります。

    29. [保存] をクリックして条件を適用します。

    30. [条件の編集] パネルを閉じたら、[権限の編集] パネルでもう一度 [保存] をクリックし、IAM ポリシーを更新します。

    条件エディタ:

    1. [条件エディタ] タブをクリックし、次の式を入力します。

      (resource.type == "compute.googleapis.com/Disk" &&
      resource.name.startsWith("projects/project-123/regions/us-central1/disks/devAccess")) ||
      (resource.type == "compute.googleapis.com/Instance" &&
      resource.name.startsWith("projects/project-123/zones/us-central1-a/instances/devAccess")) ||
      (resource.type != "compute.googleapis.com/Disk" &&
      resource.type != "compute.googleapis.com/Instance")
    2. 式を入力したら、右上のテキスト ボックスの上にある [リンターを実行] をクリックして、CEL 構文を lint にすることもできます。

    3. [保存] をクリックして条件を適用します。

    4. [条件の編集] パネルを閉じたら、[権限の編集] パネルでもう一度 [保存] をクリックし、IAM ポリシーを更新します。

gcloud

IAM ポリシーは、読み取り、変更、書き込みのパターンを使用して設定されます。

gcloud projects get-iam-policy コマンドを実行し、プロジェクトの現在の IAM ポリシーを取得します。次の例では、JSON バージョンのポリシーがディスク上のパスにダウンロードされます。

コマンド:

gcloud projects get-iam-policy project-id --format=json > filepath

JSON 形式の IAM ポリシーがダウンロードされます。

{
  "bindings": [
    {
      "members": [
        "user:project-owner@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 1
}

リソース名接頭辞の条件を指定してポリシーを構成するには、次の強調表示された条件式を追加します。gcloud ツールのバージョン 263.0.0 以降を使用していない場合は、version 値を 3 に更新していることを確認ください。新しいバージョンの gcloud ツールを使用している場合は、最大のポリシー値が自動的に設定されます。

{
  "bindings": [
    {
      "members": [
        "user:project-owner@example.com"
      ],
      "role": "roles/owner"
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin",
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ],
  "etag": "BwWKmjvelug=",
  "version": 3
}

次に、gcloud projects set-iam-policy コマンドを実行して新しいポリシーを設定します。

gcloud projects set-iam-policy project-id filepath

新しい条件付きロール バインディングでは、次の方法で devs@example.com 権限を付与します。

  • リソース名が devAccess で始まる場合に限り、ディスクとインスタンスのすべての権限が付与されます。

  • インスタンス管理者ロールの他のすべての権限は、他のすべてのリソースタイプに対して付与されます。

REST

読み取り、変更、書き込みのパターンを使用して、特定のリソースへのアクセスを許可します。

まず、プロジェクトの IAM ポリシーを読み取ります。

Resource Manager API の projects.getIamPolicy メソッドは、プロジェクトの IAM ポリシーを取得します。

後述のリクエストのデータを使用する前に、次のように置き換えます。

  • project-id: Google Cloud プロジェクト ID
  • policy-version: 返されるポリシー バージョン。リクエストでは、最新のポリシー バージョン(ポリシー バージョン 3)を指定する必要があります。詳細については、ポリシーの取得時にポリシー バージョンを指定するをご覧ください。

HTTP メソッドと URL:

POST https://cloudresourcemanager.googleapis.com/v1/projects/project-id:getIamPolicy

JSON 本文のリクエスト:

{
  "options": {
    "requestedPolicyVersion": policy-version
  }
}

リクエストを送信するには、次のいずれかのオプションを展開します。

次のような JSON レスポンスが返されます。

{
  "version": 1,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:project-owner@example.com"
      ]
    },
    {
      "members": [
        "group:devs@example.com"
      ],
      "role": "roles/compute.instanceAdmin"
    }
  ]
}

次に、特定のリソースへのアクセスを許可するようにポリシーを変更します。version フィールドを値 3 に変更します。

{
  "version": 3,
  "etag": "BwWKmjvelug=",
  "bindings": [
    {
      "role": "roles/owner",
      "members": [
        "user:project-owner@example.com"
      ]
    },
    {
      "role": "roles/compute.instanceAdmin",
      "members": [
        "group:devs@example.com"
      ],
      "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
      }
    }
  ]
}

最後に、更新されたポリシーを記述します。

Resource Manager API の projects.setIamPolicy メソッドは、リクエストのポリシーをプロジェクトの新しい IAM ポリシーとして設定します。

後述のリクエストのデータを使用する前に、次のように置き換えます。

  • project-id: Google Cloud プロジェクト ID

HTTP メソッドと URL:

POST https://cloudresourcemanager.googleapis.com/v1/projects/project-id:setIamPolicy

JSON 本文のリクエスト:

{
  "policy": {
    "version": 3,
    "etag": "BwWKmjvelug=",
    "bindings": [
      {
        "role": "roles/owner",
        "members": [
          "user:project-owner@example.com"
        ]
      },
      {
        "role": "roles/compute.instanceAdmin",
        "members": [
          "group:devs@example.com"
        ],
        "condition": {
          "title": "Dev_access_only",
          "description": "Only access to devAccess* VMs",
          "expression":
            "(resource.type == 'compute.googleapis.com/Disk' &&
            resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
            (resource.type == 'compute.googleapis.com/Instance' &&
            resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
            (resource.type != 'compute.googleapis.com/Instance' &&
            resource.type != 'compute.googleapis.com/Disk')"
        }
      }
    ]
  }
}

リクエストを送信するには、次のいずれかのオプションを展開します。

レスポンスには、更新されたポリシーが含まれます。


リソース名からの値の抽出

上記の例では、リソース名またはリソース名の先頭と別の値のブール値比較を示しています。ただし、ある値を、リソース名の先頭にない部分と比較する必要がある場合もあります。

たとえば、extract() 関数を使用して抽出テンプレートを指定し、リソース名の関連部分を文字列として抽出できます。必要に応じて、抽出した文字列をタイムスタンプなどの別の型に変換できます。リソース名から値を抽出すると、その値を他の値と比較できます。

次の例では、extract() 関数を使用する条件式を示しています。extract() 関数の詳細については、IAM Conditions 属性のリファレンスをご覧ください。

例: 過去 30 日間の注文の照合

注文情報を複数の Cloud Storage バケットに格納し、各バケット内のオブジェクトが日付順に整理されているとします。一般的なオブジェクト名は、次の例のようになります。

projects/_/buckets/acme-orders-aaa/data_lake/orders/order_date=2019-11-03/aef87g87ae0876

過去 30 日間のすべての注文を照合するとします。次の条件は、この注文と一致します。duration()date() 関数を使用して、リクエスト日から 30 日分(2,592,000 秒)を減算して、そのタイムスタンプを注文日と比較します。

request.time - duration('2592000s') < date(resource.name.extract('/order_date={date_str}/'))

date()duration() 関数の詳細については、日時属性の参照をご覧ください。

例: 任意の場所での Compute Engine VM の照合

名前が dev- で始まる任意のロケーションの Compute Engine VM を照合する必要があるとします。VM のリソース名は、projects/project-id/zones/zone-id/instances/instance-name のような形式を使用します。インスタンス名が文字列 dev- で始まる場合、次の条件は true と評価されます。

resource.name.extract('/instances/{name}/').startsWith('dev-')

中かっこ内のテキストは、比較のために抽出されたリソース名の部分を識別します。この例では、抽出テンプレートは文字列 /instances/ が最初に出現する場所から文字列 / が次に出現する場所までの間のすべての文字を抽出します。

リソースベースの条件に関する重要な使用上の考慮事項

リソースベースの条件を追加する場合は、条件がメンバーの権限にどのように影響するかを考慮することが重要です。

カスタムロール

カスタムロールにかかわる次の例を考えてみましょう。管理者には、カスタムロールを作成して、アクセス権を付与し、VM インスタンスを作成するという目的があります。ただし、ユーザーに許可するのは、リソース名が名前の接頭辞 staging で始まるプロジェクト内に、同じ名前の接頭辞のディスクを使用して、VM インスタンスを作成することのみです。

この目的のためには、付与されたロールに VM インスタンスを作成するための必要な権限が含まれていることを確認します。これは、ディスク リソースとインスタンス リソース タイプに対する権限です。次に、条件式でリソース名のディスクとインスタンスの両方を確認します。この 2 つのタイプ以外では、ロールの他の権限は付与されません。

次の条件式では、予期しない動作が発生します。Compute Engine VM を操作する権限がブロックされています。

resource.type == 'compute.googleapis.com/Disk' &&
 resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')

次の条件式にはディスクとインスタンスの両方が含まれ、この 2 つのタイプのリソース名に基づいてアクセスが管理されます。

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging'))

次の条件式にはディスクとインスタンスの両方が含まれ、この 2 つのタイプのリソース名に基づいてアクセスが管理されます。他のリソースタイプについては、リソース名に関係なくこの条件式でロールが付与されます。

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/staging')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/staging')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')

親のみの権限

Google Cloud のリソース階層では、子リソースに影響するロールの一部の権限は、親レベルでのみ適用されます。たとえば、Cloud KMS の暗号鍵をリストするには、ユーザーには、鍵自体ではなく暗号鍵を含むキーリングに対する cloudkms.cryptokeys.list 権限が付与される必要があります。このような種類の権限は親限定権限と呼ばれ、list オペレーションにのみ適用されます。

条件を使用する際の *.*.list 権限へのアクセス権を適切に付与するには、一覧表示するターゲット リソースの親リソースタイプに従って、条件式で resource.service 属性と resource.type 属性を設定する必要があります。

以下の例を考えてみましょう。前述の Compute Engine の例を使用すると、次の式では、compute.disks.listcompute.instances.list 権限へのアクセス権が防止されます。この権限が防止されるリソースは、cloudresourcemanager.googleapis.com/Projectresource.type 属性値を持つためです。

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess'))

一般的に、この list 権限は、リソースに対する通常の操作のための他の権限とともに付与されます。この場合の付与の範囲を増やすには、cloudresourcemanager.googleapis.com/Project タイプの範囲のみを拡張するか、インスタンスまたはディスク以外のすべての権限に範囲を拡張します。

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 resource.type == 'cloudresourcemanager.googleapis.com/Project'

または

(resource.type == 'compute.googleapis.com/Disk' &&
  resource.name.startsWith('projects/project-123/regions/us-central1/disks/devAccess')) ||
 (resource.type == 'compute.googleapis.com/Instance' &&
  resource.name.startsWith('projects/project-123/zones/us-central1-a/instances/devAccess')) ||
 (resource.type != 'compute.googleapis.com/Disk' &&
  resource.type != 'compute.googleapis.com/Instance')