混合信頼フリート ワークロードから Google Cloud API に対する認証を行う

このページでは、フリート全体で混合信頼モデルを利用しているフリートから Compute Engine API や AI Platform API などのGoogle Cloud API に対して認証を行うようアプリケーションを構成する方法について説明します。フリート全体で共有信頼モデルを使用している場合は、共有信頼フリート ワークロードから Google Cloud API に対して認証するをご覧ください。

このページは、フリート ワークロードから Google CloudAPI に対してプログラムで認証を行うプラットフォーム管理者や運用担当者、セキュリティ エンジニアを対象としています。 Google Cloudのドキュメントで使用されているユーザーロールとタスク例の詳細については、一般的な GKE Enterprise ユーザーロールとタスクをご覧ください。

このページを読む前に、次の内容をよく理解しておいてください。

混合信頼環境のフリート Workload Identity 連携について

フリート Workload Identity 連携を使用すると、Google Cloud API とリソースに対する IAM ロールを、フリート内のエンティティ(特定の Namespace 内のワークロードなど)に付与できます。デフォルトでは、フリートホスト プロジェクトは Google が管理する Workload Identity プールを使用して、フリート全体のエンティティの ID をプロビジョニングします。ただし、マルチテナント フリートなどの混合信頼環境や、スタンドアロン クラスタを実行するフリート ホスト プロジェクトでは、ワークロードとクラスタのサブセットに個別のセルフマネージド Workload Identity プールを構成することをおすすめします。

セルフマネージド Workload Identity プールを使用するエンティティの IAM ポリシーの ID は、フリート ホスト プロジェクトの Google マネージド Workload Identity プールを使用するエンティティの ID とは異なります。これにより、特定のフリート Namespace 内のプリンシパルにアクセス権を付与しても、ID に一致する他のプリンシパルにアクセス権が意図せず付与されることはありません。

セルフマネージド Workload Identity プールでは、チームスコープを使用する必要があります。チームスコープを使用すると、フリート リソースのサブセットへのアクセスをチームごとに制御できます。特定のチームスコープを特定のフリート メンバー クラスタにバインドすると、そのチームがクラスタにワークロードをデプロイできるようになります。チームスコープ内では、チームメンバーはフリート Namespace にのみワークロードをデプロイできます。

セルフマネージド Workload Identity プールを使用してチームスコープのワークロードに ID を提供するメリットは次のとおりです。

  • フリート Namespace のエンティティへのアクセス権が、他の Namespace またはクラスタ内のエンティティに意図せず適用されないようにします。
  • チームスコープにバインドし、それらのクラスタでセルフマネージド プールを ID プロバイダとして設定することで、セルフマネージド プールから ID を取得するようにフリートクラスタのセットを構成します。
  • 特定のクラスタでセルフマネージド プールを ID プロバイダとしてのみ設定し、チームスコープのバインディング クラスタのサブセットを構成して、セルフマネージド プールから ID を取得します。

混合信頼環境での ID の同一性の例

次のシナリオを考えてみます。

  • frontend-clusterfinance-cluster の 2 つのフリート メンバー クラスタがあります。
  • セルフマネージド Workload Identity プールを構成していません。
  • チームスコープに finance-team チームスコープと finance-ns フリート Namespace を作成します。
  • finance-cluster クラスタを finance-team チームスコープにバインドします。
  • finance-ns フリート Namespace の finance-sa Kubernetes ServiceAccount に IAM ロールを付与します。

次の条件を満たすワークロードは、同じ ID を共有します。

  • finance-ns フリート Namespace で実行します。
  • finance-sa ServiceAccount を使用します。

ただし、frontend-cluster クラスタ内のユーザーが finance-ns Kubernetes Namespace と finance-sa ServiceAccount を作成すると、finance-cluster クラスタ内のワークロードと同じ ID が取得されます。これは、フリート全体がフリート ホスト プロジェクトの Google マネージド Workload Identity プールを使用し、プリンシパル ID でホストクラスタが指定されていないためです。

上記のシナリオを次のように変更してみましょう。

  • フリートにセルフマネージド Workload Identity プールを設定します。
  • Google マネージド プールではなく、セルフマネージド プールから ID を取得するように finance-cluster クラスタを構成します。
  • Google マネージド プールではなく、プリンシパル ID にセルフマネージド プールを指定する IAM ロール付与を作成します。

finance-clusterfinance-ns フリート Namespace で実行されるワークロードは、セルフマネージド プールから ID を取得するようになりました。ただし、frontend-cluster クラスタの finance-ns Kubernetes Namespace 内のエンティティは、引き続きフリート ホスト プロジェクトの Google マネージド Workload Identity プールから ID を取得します。

この変更により、次のようなメリットがあります。

  • finance-ns フリート Namespace 内のエンティティにロールを明示的に付与できます。
  • frontend-cluster クラスタ内のエンティティは、Google マネージド Workload Identity プールから ID を取得するため、frontend-cluster クラスタのエンティティが同じアクセス権を取得することはできません。

始める前に

  • 次のコマンドライン ツールがインストールされていることを確認します。

    • 最新バージョンの Google Cloud CLI( Google Cloudとやり取りするためのコマンドライン ツールである gcloud が含まれる)。
    • kubectl

    Google Cloudを操作するシェル環境として Cloud Shell を使用する場合は、これらのツールがインストールされます。

  • プロジェクトで使用する gcloud CLI が初期化されていることを確認します。

要件

フリートでは、チームスコープやフリートの Namespace など、フリートチーム管理機能を使用する必要があります。このページの手順では、チームスコープとフリートの Namespace の例を構成する方法について説明します。

クラスタを準備する

フリートのアプリケーションが連携 ID を受け取るためには、アプリケーションが実行されているクラスタがフリートに登録され、フリート Workload Identity 連携を使用するように正しく構成されている必要があります。以降のセクションでは、さまざまなタイプのクラスタにフリート Workload Identity 連携を設定する方法について説明します。

GKE

GKE クラスタの場合は、次の操作を行います。

  1. Google Kubernetes Engine クラスタで Workload Identity Federation for GKE を有効にします(まだ有効になっていない場合)。
  2. フリートにクラスタを登録します。

クラスタの作成プロセスとフリートの登録プロセスで、Workload Identity Federation for GKE を有効にすることもできます。

Google Cloudの外部にあるクラスタ

次のタイプのクラスタでは、フリート Workload Identity 連携が自動的に有効になり、クラスタの作成時にフリートに登録されます。

  • Google Distributed Cloud on VMware(ソフトウェアのみ)
  • Google Distributed Cloud on Bare Metal(ソフトウェアのみ)
  • GKE on AWS
  • GKE on Azure

接続クラスタ

GKE Multi-Cloud API を使用して登録された EKS 接続クラスタおよび AKS 接続クラスタは、デフォルトで有効になっているフリート Workload Identity 連携に登録されています。必要な要件を満たしている場合、フリート Workload Identity 連携を有効にしてその他の接続クラスタを登録できます。クラスタの登録にあるクラスタタイプに応じた手順に沿います。

IAM Workload Identity プールを設定する

このセクションでは、フリート ホスト プロジェクトに新しい IAM Workload Identity プールを作成し、フリート サービス エージェントに新しいプールへのアクセス権を付与します。

  1. Workload Identity プールを作成します。

    gcloud iam workload-identity-pools create POOL_NAME \
        --location=global \
        --project=POOL_HOST_PROJECT_ID \
        --mode=TRUST_DOMAIN
    

    次のように置き換えます。

    • POOL_NAME: 新しい Workload Identity プールの名前。
    • POOL_HOST_PROJECT_ID: セルフマネージド Workload Identity プールを作成するプロジェクトのプロジェクト ID。フリート ホスト プロジェクトを含む任意の Google Cloud プロジェクトを使用できます。
  2. 新しい Workload Identity プールに対する IAM Workload Identity プール管理者ロールroles/iam.workloadIdentityPoolAdmin)をフリート サービス エージェントに付与します。

    gcloud iam workload-identity-pools add-iam-policy-binding POOL_NAME \
        --project=POOL_HOST_PROJECT_ID \
        --location=global \
        --member=serviceAccount:service-FLEET_HOST_PROJECT_NUMBER@gcp-sa-gkehub.iam.gserviceaccount.com \
        --role=roles/iam.workloadIdentityPoolAdmin \
        --condition=None
    

    FLEET_HOST_PROJECT_NUMBER は、フリートホスト プロジェクトのプロジェクト番号に置き換えます。

フリート構成にセルフマネージド プールを追加する

このセクションでは、フリート Workload Identity 連携でセルフマネージド プールを有効にし、作成したプールをフリート構成に追加します。このセクションでは、新しいチームスコープとフリートの Namespace を作成する手順についても説明します。フリートにすでにチームスコープとフリートの Namespace が構成されている場合は、これらの手順をスキップしてください。

  1. フリートレベルでフリート Workload Identity 連携を有効にします。

    gcloud beta container fleet workload-identity enable \
      --project=FLEET_HOST_PROJECT_ID
    

    FLEET_HOST_PROJECT_ID は、フリート ホスト プロジェクトのプロジェクト ID に置き換えます。

  2. セルフマネージド Workload Identity プールをフリート構成に追加します。

    gcloud beta container fleet workload-identity scope-tenancy-pool set POOL_NAME
    

    POOL_NAME は、セルフマネージド Workload Identity プールの名前に置き換えます。この値の構文は次のとおりです。

    POOL_NAME.global.POOL_HOST_PROJECT_NUMBER.workload.id.goog
    

    ToDo

  3. 新しいチームスコープを作成します。既存のチームスコープとフリート Namespace がある場合は、Workload Identity プールの構成を確認するセクションに進みます。

    gcloud container fleet scopes create SCOPE_NAME
    

    SCOPE_NAME は、新しいチームスコープの名前に置き換えます。

  4. チームスコープに新しいフリート Namespace を作成します。

    gcloud container fleet scopes namespaces create NAMESPACE_NAME \
        --scope=SCOPE_NAME
    

    NAMESPACE_NAME は、新しいフリート Namespace の名前に置き換えます。

  5. フリート内のクラスタをチームスコープにバインドします。

    gcloud container fleet memberships bindings create BINDING_NAME \
        --membership=FLEET_CLUSTER_NAME \
        --location=global \
        --scope=SCOPE_NAME
    

    次のように置き換えます。

    • BINDING_NAME: 新しいメンバーシップ バインディングの名前。
    • FLEET_CLUSTER_NAME: チームスコープにバインドする既存のフリート クラスタの名前。

Workload Identity プールの構成を確認する

このセクションでは、セルフマネージド Workload Identity プールの構成が成功したことを確認します。

  1. フリート メンバーシップ構成を記述します。

    gcloud container fleet memberships describe FLEET_CLUSTER_NAME \
        --location=global
    

    FLEET_CLUSTER_NAME は、フリート内の任意のチームスコープにバインドされている既存のフリート クラスタの名前に置き換えます。

    出力は次のようになります。

    authority:
    ...
      scopeTenancyIdentityProvider: https://gkehub.googleapis.com/projects/FLEET_HOST_PROJECT_ID/locations/global/memberships/FLEET_CLUSTER_NAME
      scopeTenancyWorkloadIdentityPool: POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
      workloadIdentityPool: FLEET_HOST_PROJECT_ID.svc.id.goog
    ...
    

    この出力のフィールドは次のとおりです。

    • scopeTenancyIdentityProvider: チームスコープ内のフリート Namespace で実行されるワークロードの ID プロバイダ。この値は、クラスタのリソース ID です。
    • scopeTenancyWorkloadIdentityPool: チームスコープ内のフリート Namespace 内のワークロードが ID を取得する Workload Identity プール。値は、POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog 形式のセルフマネージド Workload Identity プールです。
    • workloadIdentityPool: フリート ホスト プロジェクトの Google マネージド Workload Identity プールの名前。フリート内の他のすべてのワークロードは、デフォルトでこのプールから ID を取得します。
  2. 省略可: Workload Identity プールに、フリートの Namaspace と同じ名前の Namaspace があるかどうかを確認します。

    gcloud iam workload-identity-pools namespaces list \
        --workload-identity-pool=POOL_NAME \
        --location=global
    

    出力は次のようになります。

    ---
    description: Fleet namespace NAMESPACE_NAME
    name: projects/FLEET_HOST_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME/namespaces/NAMESPACE_NAME
    state: ACTIVE
    
  3. 省略可: ワークロード ID プールの Namaspace に、フリートの Namaspace を参照する構成証明ルールがあるかどうかを確認します。

    gcloud iam workload-identity-pools namespaces list-attestation-rules NAMESPACE_NAME \
        --workload-identity-pool=POOL_NAME \
        --location=global
    

    出力は次のようになります。

    ---
    googleCloudResource:
    //gkehub.googleapis.com/projects/FLEET_HOST_PROJECT_NUMBER/name/locations/global/scopes/-/namespaces/NAMESPACE_NAME
    

フリートは、セルフマネージド Workload Identity プールを使用して、フリート Namaspace で実行されるワークロードの ID を取得できるようになりました。セルフマネージド プールの使用を開始するには、次のセクションで説明するように、特定のクラスタが ID を取得する方法を構成します。

ワークロードで ID にセルフマネージド プールを使用する

ワークロードでセルフマネージド プールを使用するようにするには、Kubernetes ConfigMap を使用して、フリート メンバー クラスタ内の特定のフリート Namespace を構成します。このクラスタごと、Namespace ごとの構成により、フリート Namespace 全体から、特定のクラスタ内の特定のフリート Namespace で実行されるワークロードへのアクセス権限の付与範囲をさらに狭めることができます。

  1. フリート メンバー クラスタに接続します。

    gcloud container clusters get-credentials FLEET_CLUSTER_NAME \
        --project=CLUSTER_PROJECT_ID \
        --location=CLUSTER_LOCATION
    

    次のように置き換えます。

    • FLEET_CLUSTER_NAME: すでにチームスコープにバインドされているフリート メンバー クラスタの名前。
    • CLUSTER_PROJECT_ID: クラスタ プロジェクトのプロジェクト ID。
    • CLUSTER_LOCATION: クラスタのロケーション。
  2. セルフマネージド Workload Identity プールのフルネームを取得します。後で必要になります。

    kubectl get membership membership -o json | jq -r ".spec.scope_tenancy_workload_identity_pool"
    

    出力は次のようになります。

    POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
    
  3. チームスコープの ID プロバイダの名前を取得します。後で必要になります。

    kubectl get membership membership -o json | jq -r ".spec.scope_tenancy_identity_provider"
    

    出力は次のようになります。

    https://gkehub.googleapis.com/projects/FLEET_HOST_PROJECT_ID/locations/global/memberships/FLEET_CLUSTER_NAME
    
  4. テキスト エディタで、ConfigMap の次の YAML マニフェストを self-managed-pool.yaml として保存します。

    kind: ConfigMap
    apiVersion: v1
    metadata:
      namespace: NAMESPACE_NAME
      name: google-application-credentials
    data:
      config: |
        {
          "type": "external_account",
          "audience": "identitynamespace:SELF_MANAGED_POOL_FULL_NAME:IDENTITY_PROVIDER",
          "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
          "token_url": "https://sts.googleapis.com/v1/token",
          "credential_source": {
            "file": "/var/run/secrets/tokens/gcp-ksa/token"
          }
        }
    

    次のように置き換えます。

    • NAMESPACE_NAME: フリート Namespace の名前。
    • SELF_MANAGED_POOL_FULL_NAME: このセクションの前の手順の出力から取得したセルフマネージド Workload Identity プールの完全な名前。例: example-pool.global.1234567890.workload.id.goog
    • IDENTITY_PROVIDER: このセクションの前の手順の出力から取得した ID プロバイダ名。例: https://gkehub.googleapis.com/projects/1234567890/locations/global/memberships/example-cluster.
  5. クラスタに ConfigMap をデプロイします。

    kubectl create -f self-managed-pool.yaml
    

ConfigMap をデプロイすると、その Namespace 内のワークロードがセルフマネージド Workload Identity プールを使用して ID を取得する必要があることが GKE に示されます。

プリンシパルに IAM ロールを付与する

このセクションでは、フリート Namespace に Kubernetes ServiceAccount を作成し、ServiceAccount に IAM ロールを付与します。この ServiceAccount を使用する Pod は、ロールを付与する Google Cloud リソースにアクセスできます。

  1. フリート Namespace に Kubernetes ServiceAccount を作成します。

    kubectl create serviceaccount SERVICEACCOUNT_NAME \
        --namespace=NAMESPACE_NAME
    

    次のように置き換えます。

    • SERVICEACCOUNT_NAME: 新しい ServiceAccount の名前。
    • NAMESPACE_NAME: フリート Namespace の名前。
  2. ServiceAccount に IAM ロールを付与します。次のコマンドの例では、バケットに対する Storage オブジェクト閲覧者のロールroles/storage.objectViewer)を ServiceAccount に付与します。

    gcloud storage buckets add-iam-policy-binding gs://BUCKET_NAME \
        --member=principal://iam.googleapis.com/projects/FLEET_HOST_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog/subject/ns/NAMESPACE_NAME/sa/SERVICEACCOUNT_NAME \
        --role=roles/storage.objectViewer \
        --condition=None
    

member フラグには、作成した新しい ServiceAccount のプリンシパル ID が含まれます。ワークロードが Google CloudAPI に送信するリクエストでは、連携アクセス トークンを使用します。この連携アクセス トークンには、リクエストを送信するエンティティのプリンシパル ID が含まれています。ターゲット リソースに対するロールを付与する許可ポリシーのプリンシパルが、連携アクセス トークンのプリンシパルと一致する場合、認証と認可を続行できます。

セルフマネージド プールを使用するワークロードをデプロイする

フリート Namespace に適用する Kubernetes マニフェストは、セルフマネージド プールから ID を取得するように構成する必要があります。自身がデプロイして、 Google Cloud API を呼び出す必要があるワークロードの場合は、次のフィールドを含める必要があります。

  • metadata.namespace: フリート Namespace の名前。
  • spec.serviceAccountName: フリート Namespace 内の Kubernetes ServiceAccount の名前。
  • spec.containers.env: アプリケーションのデフォルト認証情報(ADC)ファイルのパスを指定する GOOGLE_APPLICATION_CREDENTIALS という名前の環境変数。
  • spec.containers.volumeMounts: コンテナが ServiceAccount のベアラー トークンを使用できるようにする読み取り専用ボリューム。
  • spec.volumes: ServiceAccount トークンを Pod にマウントするプロジェクション ボリューム。トークンのオーディエンスは、セルフマネージド Workload Identity プールです。フリート Workload Identity 連携の構成を含む ConfigMap がボリュームのソースです。

正しく構成されたマニフェスト ファイルの例については、ワークロードからの認証を確認するをご覧ください。

ワークロードからの認証を確認する

このセクションでは、サンプルの Cloud Storage バケットの内容を一覧表示して、セルフマネージド Workload Identity プールが正しく構成されていることを確認する手順について説明します。バケットを作成して、フリート Namespace の ServiceAccount にバケットに対するロールを付与し、Pod をデプロイしてバケットにアクセスしようとします。

  1. Cloud Storage バケットを作成します。

    gcloud storage buckets create gs://FLEET_HOST_PROJECT_ID-workload-id-bucket \
        --location=LOCATION \
        --project=FLEET_HOST_PROJECT_ID
    
  2. フリート Namespace の ServiceAccount にバケットに対する roles/storage.objectViewer ロールを付与します。

    gcloud storage buckets add-iam-policy-binding gs://FLEET_HOST_PROJECT_ID-workload-id-bucket \
        --condition=None \
        --role=roles/storage.objectViewer \
        --member=principal://iam.googleapis.com/projects/FLEET_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog/subject/ns/NAMESPACE_NAME/sa/SERVICEACCOUNT_NAME
    

    次のように置き換えます。

    • FLEET_HOST_PROJECT_NUMBER: フリート ホスト プロジェクトのプロジェクト番号。
    • POOL_NAME: セルフマネージド Workload Identity プールの名前。
    • NAMESPACE_NAME: Pod を実行するフリート Namespace の名前。
    • SERVICEACCOUNT_NAME: Pod が使用する Kubernetes ServiceAccount の名前。
  3. 次のマニフェストを pod-bucket-access.yaml として保存します。

    apiVersion: v1
    kind: Pod
    metadata:
      name: bucket-access-pod
      namespace:  NAMESPACE_NAME
    spec:
      serviceAccountName: SERVICEACCOUNT_NAME
      containers:
      - name: sample-container
        image: google/cloud-sdk:slim
        command: ["sleep","infinity"]
        env:
        - name: GOOGLE_APPLICATION_CREDENTIALS
          value: /var/run/secrets/tokens/gcp-ksa/google-application-credentials.json
        volumeMounts:
        - name: gcp-ksa
          mountPath: /var/run/secrets/tokens/gcp-ksa
          readOnly: true
      volumes:
      - name: gcp-ksa
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              path: token
              audience: POOL_NAME.global.FLEET_HOST_PROJECT_NUMBER.workload.id.goog
              expirationSeconds: 172800
          - configMap:
              name: my-cloudsdk-config
              optional: false
              items:
              - key: "config"
                path: "google-application-credentials.json"
    

    次のように置き換えます。

    • NAMESPACE_NAME: Pod を実行するフリート Namespace の名前。
    • SERVICEACCOUNT_NAME: Pod が使用する Kubernetes ServiceAccount の名前。
    • POOL_NAME: セルフマネージド Workload Identity プールの名前。
    • FLEET_HOST_PROJECT_NUMBER: フリート ホスト プロジェクトのプロジェクト番号。
  4. Pod をクラスタにデプロイします。

    kubectl apply -f pod-bucket-access.yaml
    
  5. Pod でシェル セッションを開きます。

    kubectl exec -it bucket-access-pod -n NAMESPACE_NAME -- /bin/bash
    
  6. バケット内にあるオブジェクトを一覧表示してみます

    curl -X GET -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
        "https://storage.googleapis.com/storage/v1/b/FLEET_HOST_PROJECT_ID-workload-id-bucket/o"
    

    次のような出力が表示されます。

    {
      "kind": "storage#objects"
    }
    

必要に応じて、異なるフリート メンバー クラスタ内の類似の Namespace と ServiceAccount が同じ ID をアサートできないことを確認できます。フリート Workload Identity 連携を使用しているクラスタで、フリート Namaspace またはセルフマネージド プール構成がない場合は、次の操作を行います。

  1. セルフマネージド Workload Identity プールを設定するフリート Namaspace と同じ名前の新しい Kubernetes Namespace を作成します。
  2. 前のセクションで IAM ロールを付与した ServiceAccount と同じ名前で新しい Kubernetes ServiceAccount を作成します。
  3. 同じ ServiceAccount と Namespace を使用する Pod をデプロイします。ただし、spec.volumes.projected.sources.serviceAccountToken フィールドで Google マネージド Workload Identity プールを指定します。このプールの構文は次のとおりです。

    FLEET_HOST_PROJECT_ID.svc.id.goog
    
  4. Pod のシェル セッションから Cloud Storage バケットにアクセスしてみます。

Google マネージド Workload Identity プールを使用する Pod のプリンシパル ID は、セルフマネージド プールを使用する Pod のプリンシパル ID とは異なるため、出力は 401: Unauthorized エラーになります。

次のステップ