Workload Identity 連携の証明書ベースのアクセスを構成する

このドキュメントでは、X.509 証明書を使用して Workload Identity 連携の証明書ベースのアクセスを構成する方法について説明します。

証明書ベースのアクセスでは、相互 TLS(mTLS)を使用して、TLS handshake 中にクライアントとサーバーの両方を認証します。このプロセスでは、mTLS バインディングはトランスポート コンテキストに基づくポリシーを組み込み、TLS セッション内のクライアント証明書の状態を使用して認可の決定を行います。

X.509 Workload Identity 連携の場合、mTLS バインディングにより、認証フロー全体が信頼できるワークロードに安全に結び付けられます。認証は特定の信頼できるエンドポイントにバインドされるため、認証情報の盗難のリスクが軽減されます。

Workload Identity 連携の構成の概要(証明書ベースのアクセス)

Workload Identity 連携の証明書ベースのアクセスを構成するプロセスについて、概要を説明します。

  1. X.509 証明書の信頼アンカーを使用して信頼を構成し、Workload Identity 連携を確立します。

  2. 証明書ベースのアクセス用のアクセスレベルを作成します。

  3. mTLS バインディングを適用するコンテキストアウェア アクセス ポリシーにアクセスレベルを追加します。

始める前に

次の前提条件を満たしていることを確認します。

  • Google Cloud CLI の最新バージョン

    Google Cloud CLI の最新バージョンに更新するには、次のコマンドを実行します。

    gcloud components update
    

    Google Cloud CLI をインストールする必要がある場合は、Google Cloud CLI をインストールするをご覧ください。

  • X.509 証明書の信頼アンカーを使用する Workload Identity 連携構成

  • この機能を使用するには、次のフォームに記入して、許可リストに追加してください。許可リストのリクエスト フォーム許可リストに追加されたら、ご連絡いたします。

証明書のアクセスレベルを作成する

  1. mTLS アクセスレベルを作成します。mTLS アクセスレベルは、リソースへのアクセスを決定するときに証明書を検証します。

    コンソール

    Access Context Manager で、カスタム アクセスレベルを作成し、CEL 式フィールドに request.auth.matchesMtlsTokens(origin) == true という式を入力します。

    gcloud

    カスタム アクセスレベルを作成するには、次のコマンドを実行します。

       gcloud access-context-manager levels create ACCESS_LEVEL_NAME 
    --title=TITLE
    --custom-level-spec=FILE
    --description=DESCRIPTION
    --policy=POLICY_NAME

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

    • ACCESS_LEVEL_NAME: アクセスレベルの名前。
    • TITLE: アクセスレベルのタイトル。
    • FILE: 次のコンテンツを含む YAML ファイル: request.auth.matchesMtlsTokens(origin) == true
    • DESCRIPTION: アクセスレベルの説明。
    • POLICY_NAME: アクセス ポリシーの名前。
  2. 作成したアクセスレベルを環境変数にエクスポートします。この変数は以降のステップで使用されます。

      export ACCESS_LEVEL_ID=ACCESS_LEVEL_ID
      

    ACCESS_LEVEL_ID は、アクセスレベル名(accessPolicies/12345/accessLevels/acl_1 など)に置き換えます。

Workload Identity プールのコンテキストアウェア アクセス バインディングを作成する

  1. 次の環境変数を設定します。

    export ORG_ID=ORG_ID
    export CALLER_PROJECT_ID=CALLER_PROJECT_ID
    export FEDERATED_PRINCIPAL=FEDERATED_PRINCIPAL
    

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

    • ORG_ID: 組織の ID。
    • CALLER_PROJECT_ID: API の呼び出しに使用するプロジェクトの ID。
    • FEDERATED_PRINCIPAL: コンテキストアウェア アクセス ポリシーに準拠する Workload Identity プールの ID プリンシパルの名前。次のいずれかのオプションを使用できます。

      形式が principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT_ATTRIBUTE_VALUE の単一の ID

      または

      プール内のすべての ID(形式) - principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*

    gcloud

    gcloud alpha access-context-manager cloud-bindings create \
    --organization=ORG_ID \
    --federated-principal=FEDERATED_PRINCIPAL \
    --level=ACCESS_LEVEL_ID
    --dry-run-level=DRY_RUN_ACCESS_LEVEL_ID
    

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

    • ACCESS_LEVEL_ID: アクセスレベルの名前。
    • DRY_RUN_ACCESS_LEVEL_ID: ドライラン アクセスレベルの名前。最初にドライラン ポリシー バインディングを有効にして、既存のトラフィックに及ぼす可能性のある影響を把握することをおすすめします。

    curl

    1. コンテキストアウェア アクセス バインディングを含む JSON ファイルを作成します。

      フィールドは繰り返されますが、リクエストで指定できるアクセスレベルは 1 つのみです。次のタイプの連携プリンシパルを使用できます。

      • 単一の ID: principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT_ATTRIBUTE_VALUE
      • プール内のすべての ID: principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/*
      echo { \
        \"principal\": { \
          \"federatedPrincipal\": \"${FEDERATED_PRINCIPAL:?}\" \
        },\
        \"accessLevels\": [\"${ACCESS_LEVEL_ID:?}\"] \
      } \
      >> request.json
      
    2. curl を使用して、次の HTTP リクエストを送信します。

      curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X POST \
         -H "Authorization: Bearer $(gcloud auth print-access-token)" \
         -H "Content-Type: application/json; charset=utf-8" \
         -d @request.json \
       "https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings"
      

Google Cloud クライアント ライブラリを使用して承認する

Google Cloud クライアント ライブラリを使用して Workforce Identity 連携ワークロードを承認する手順は次のとおりです。

  1. Workforce Identity 連携認証用に構成されたアプリケーションのデフォルト認証情報(ADC)ファイルを作成します。

    gcloud iam workload-identity-pools create-cred-config IDENTITY_POOL_ID \
    --credential-cert-path WORKLOAD_CERTIFICATE_PATH \
    --credential-cert-private-key-path WORKLOAD_KEY_PATH \
    --output-file ADC_FILE_OUTPUT_PATH
    

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

    • IDENTITY_POOL_ID: Workload Identity プールの ID。
    • WORKLOAD_CERTIFICATE_PATH: ワークロードの証明書ファイルのパス。
    • WORKLOAD_KEY_PATH: ワークロードの秘密鍵ファイルのパス。
    • ADC_FILE_OUTPUT_PATH: ADC ファイルの出力パス。

    このコマンドは、gcloud CLI のデフォルトの構成ディレクトリに証明書構成ファイルも生成します。証明書構成ファイルは、初期認証をサポートし、 Google Cloud リソースに対する後続のリクエストの mTLS ハンドシェイクを確立します。

  2. ADC ファイルを参照する環境変数を設定します。これにより、Google クライアント ライブラリで認証情報を検出できるようになります。

    export GOOGLE_APPLICATION_CREDENTIALS=${application_default_credentials.json}
    

    ADC ファイルを生成するときに --output-file 引数を省略した場合、このステップは省略可能です。引数を省略すると、ADC ファイルが作成され、gcloud CLI のデフォルト構成ディレクトリから読み取られます。

  3. Google Cloud API へのアクセスを確立してテストするには、次の手順を行います。Go または Python を使用できます。

    Go

    1. 次の例を使用して、golang_test.go などの Go ファイルを作成します。

      package golang_test
      
      import (
           "io"
           "log"
           "testing"
      
           "cloud.google.com/go/auth/credentials"
           "cloud.google.com/go/auth/httptransport"
      )
      
      func TestGoExample(t *testing.T) {
      
           scopes := []string{
                   "https://www.googleapis.com/auth/pubsub", // Scope for Pub/Sub access
                   // Add other scopes as needed
           }
      
           dopts := credentials.DetectOptions{
                   Scopes: scopes,
           }
      
           // Create httptransport.Options with the scopes
           opts := &httptransport.Options{
                   DetectOpts: &dopts,
           }
           hc, err := httptransport.NewClient(opts)
           if err != nil {
                   t.Fatalf("NewHTTPClient: %v", err)
           }
      
           resp, err := hc.Get("https://pubsub.mtls.googleapis.com/v1/projects/PROJECT_ID/topics")
           if err != nil {
                   t.Fatalf("Get: %v", err)
           }
           t.Logf("Status: %s", resp.Status)
      
           t.Cleanup(func() {
                  resp.Body.Close()
           })
      
           b, err := io.ReadAll(resp.Body)
           if err != nil {
                  t.Fatal(err)
           }
           log.Println(string(b))
      }
      

      PROJECT_ID は、gcloud CLI プロジェクト ID に置き換えます。

    2. Compute Engine VM でテストを実行するには、次のコマンドを使用します。

    go mod init example.com
    go mod tidy
    go test -v golang_test.go --count=1
    

    Python

    1. 次の例を使用して、python_test.py などのテストファイルを作成します。

      import google.auth
      import google.auth.transport.requests
      import requests
      
      def test_go_example():
      # Define the required scopes for your application
      scopes = [
         "https://www.googleapis.com/auth/pubsub",  # Scope for Pub/Sub access
         # Add other scopes as needed
      ]
      
      # Obtain Application Default Credentials (ADC) with the specified scopes
      credentials, _ = google.auth.default(scopes=scopes)
      
      # Create an authorized HTTP session using the ADC credentials
      authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
      
      try:
      # Make a GET request to the Pub/Sub API endpoint
      response = authed_session.get(
          "https://pubsub.mtls.googleapis.com/v1/projects/PROJECT_ID/topics"
      )
      
      # Check if the request was successful
      response.raise_for_status()  # Raise an exception for error statuses
      
      # Log the response status and content
      print(f"Status: {response.status_code}")
      print(response.text)
      
      except requests.exceptions.RequestException as e:
      print(f"Error making the request: {e}")
      
      if __name__ == "__main__":
      test_go_example()
      

      PROJECT_ID は、gcloud CLI プロジェクト ID に置き換えます。

    2. Compute Engine VM でテストを実行するには、次の操作を行います。

      1. Python 仮想環境を設定します。
      2. 必要なライブラリをインストールします。

        pip install google-auth google-auth-httplib2 requests
        
      3. テストを実行します。

        python3 python_test.py
        

プレーン HTTP リクエストを使用して承認する

プレーン HTTP リクエストを使用して Workforce Identity 連携ワークロードを承認するには、次の操作を行います。

  1. 標準の mTLS ハンドシェイクを介して Google Cloud Security Token Service から証明書バインド アクセス トークンを取得します。

  2. Security Token Service から取得したアクセス トークンを使用して Google Cloud サービスを呼び出します。この例では、Cloud Storage にクエリを実行します。

    $ curl --key ${workload_key.pem} --cert ${workload_cert.pem} -X GET 'https://storage.mtls.googleapis.com/{replace_with_your_resources}' -H "Authorization: Bearer $ACCESS_TOKEN"
    
  3. mTLS バインディングは mTLS の使用を強制します。次のコマンドを実行して、非 mTLS 接続が未承認エラーで失敗することを確認します。

    $ curl -X GET 'https://storage.googleapis.com/{replace_with_your_resources}' -H "Authorization: Bearer $ACCESS_TOKEN"
    

ポリシー バインディングを一覧表示する

Workload Identity 連携のポリシー バインディングを一覧表示するには、次のコマンドを実行します。

gcloud

次のコマンドは、特定の組織内の特定のバインディングを一覧表示し、フェデレーション プリンシパルに適用されるバインディングをフィルタします。

gcloud alpha access-context-manager cloud-bindings list \
--organization=ORG_ID \
--filter='principal:federatedPrincipal'

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings?filter=principal%3Afederated_principal"

ポリシー バインディングを更新する

ポリシー バインディングを更新するには、新しいアクセスレベルを JSON ファイルに追加して、次のコマンドを実行します。

gcloud

gcloud alpha access-context-manager cloud-bindings update \
--binding=BINDING_ID \
--level=NEW_ACCESS_LEVEL_ID

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X PATCH \
 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
 -H "Content-Type: application/json; charset=utf-8" \
 -d @request.json \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings/${BINDING_ID:?}?updateMask=access_levels"

ポリシー バインディングを削除する

ポリシー バインディングを削除するには、次のコマンドを実行します。

gcloud

gcloud alpha access-context-manager cloud-bindings delete \
--binding=BINDING_ID

curl

curl -H "X-Goog-User-Project: ${CALLER_PROJECT_ID:?}" -X DELETE \
   -H "Authorization: Bearer $(gcloud auth print-access-token)" \
"https://accesscontextmanager.googleapis.com/v1alpha/organizations/${ORG_ID:?}/gcpUserAccessBindings/${BINDING_ID:?}"

トラブルシューティング

以下に、一般的な問題と、その解決策として推奨される対応策を示します。

  • エラー: 403 Forbidden, user does not have permission.

    対応: IAM ポリシーを確認して、ワークロード ID プールが Google Cloud リソースにアクセスできることを確認します。

  • エラー: Unauthorized_client: Could not obtain a value for google.subject from the given credential.

    アクション: バックエンドは、属性マッピングに基づいてクライアント証明書から google.subject の値を抽出できませんでした。クライアント証明書を確認して、マッピングに使用するフィールドに値があることを確認します。

  • コンテキストアウェア アクセスを有効にした後に予期しないアクセス拒否が発生した場合は、次のコマンドを使用してコンテキストアウェア アクセスのバインディングを削除することで、トラフィックのブロックをすばやく解除できます。

    gcloud alpha access-context-manager cloud-bindings delete
    

    アクセスが復元されたら、監査ログを確認して、リクエストが拒否された理由を特定します。