はじめに
本記事は、ECS Fargate 上の AI エージェントから Workload Identity Federation によるキーレス認証で Gemini を呼び出す方法を解説した記事の後編です。
前編では、ECS Fargate 上のコンテナから Google Cloud Workload Identity Federation を利用し、サービスアカウントキーを保持せずに Google Cloud のアクセストークンを取得する構成について説明しました。最終的には、Node.js と Python の双方において、ECS task role の認証情報を AWS SDK / boto3 から取得し、アクセストークンが正常に生成できることを確認しています。
前回の検証では、Google の公式手順に準拠して cred-config.json を生成し、それを環境変数 GOOGLE_APPLICATION_CREDENTIALS に指定する方式を採用しました。一方で、Node.js の google-auth-library の SDK リファレンスを確認すると、AWS ワークロードで ADC をそのまま利用する方式は EC2 前提であり、ECS / EKS / Fargate のような環境では custom AWS security credentials supplier が適したユースケースとして示されています。
該当する SDK リファレンス:
https://googleapis.dev/nodejs/google-auth-library/latest/
If you want to use AWS security credentials that cannot be retrieved using methods supported natively by this library, a custom AwsSecurityCredentialsSupplier implementation may be specified when creating an AWS client. The supplier must return valid, unexpired AWS security credentials when called by the GCP credential. Currently, using ADC with your AWS workloads is only supported with EC2. An example of a good use case for using a custom credential suppliers is when your workloads are running in other AWS environments, such as ECS, EKS, Fargate, etc.
つまり、ECS Fargate 環境で Google Cloud WIF を利用する場合、大きく分けて以下の2つの実装方式が存在します。
-
cred-config.jsonを用いる ADC 方式 - custom AWS security credentials supplier を用いる方式
本記事では、これら2つの方式の仕様と違いについて整理します。
ADC と cred-config.json の関係
ADC(Application Default Credentials)は、Google の認証ライブラリが既定の認証情報を自動的に検索・利用するための仕組みです。ローカル開発環境、Google Cloud 上の実行環境、サービスアカウントキー、あるいは Workload Identity Federation の外部アカウント設定など、環境に応じて認証情報を解決する役割を持ちます。
ADC の概要は、Google Cloud の公式ドキュメントに定義されています。
Workload Identity Federation を利用する際、gcloud iam workload-identity-pools create-cred-config コマンドによって認証設定ファイルを生成します。AWS 向けであれば --aws オプションを指定して cred-config.json を出力します。
この cred-config.json には、Google Cloud 側の WIF に必要な構成情報が格納されています。具体的には、対象の Workload Identity Pool / Provider の識別子、subject token type、借用(impersonate)するサービスアカウントの URL などの情報です。すなわち、cred-config.json は単なる補助ファイルではなく、ADC 方式において Google 認証ライブラリが WIF のトークン交換(Token Exchange)とサービスアカウントの借用を行うための必須データを含んでいます。
前回の検証では、コンテナイメージ内に cred-config.json を配置して GOOGLE_APPLICATION_CREDENTIALS でパスを指定し、アプリ側で取得した ECS task role の AWS 認証情報を環境変数に設定した上で Google 認証ライブラリを呼び出しました。この構成により、ライブラリが設定ファイルを読み込み、環境変数の AWS 認証情報を用いて Google STS へのトークン交換を実行する仕組みが成り立ちます。
ECS Fargate で ADC 方式がそのままでは利用できない理由
Google が提供する AWS 向け WIF の公式手順は、基本的に EC2 または Azure VM の利用を前提としています。そのため、create-cred-config --aws で生成される cred-config.json の credential_source は、EC2 のインスタンスメタデータサービス(IMDS)を参照する定義になります。EC2 環境であれば、IMDS を介した認証情報の解決は標準的な手法です。
しかし、ECS Fargate のコンテナでは、AWS SDK が task role の認証情報を取得する経路が異なります。通常は、ECS 側がコンテナに注入する環境変数 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI を利用し、AWS SDK がそのエンドポイントから task role の一時認証情報を取得します。
AWS 側のコンテナ認証情報の仕様は、以下のドキュメントにまとまっています。
したがって、EC2 前提の credential source に依存する設定のままでは、ECS Fargate 環境においてうまく機能しません。
前回の検証では、このギャップをアプリケーションコード側で補完しました。Node.js では AWS SDK の credential provider chain を、Python では boto3 を用いて task role の AWS 認証情報を明示的に抽出し、それを Google 認証ライブラリが認識できる環境変数へマッピングしています。これは、ADC と cred-config.json のエコシステムを活用しつつ、認証情報の取得経路のみを ECS Fargate 向けに補正したアプローチと言えます。
Web 上で同種の構成を紹介している既存の技術ブログ等でも、概ねこの方式が案内されています。
custom AWS security credentials supplier とは何か
前述の通り、Node.js の google-auth-library には custom AWS security credentials supplier という拡張機構が用意されています。これは、Google の認証ライブラリが環境から AWS 認証情報をネイティブに取得できない場合に、アプリケーション側で認証情報の供給ロジックを実装し、それを Google の AwsClient に直接注入する方式です。
SDK リファレンスでは、ECS / EKS / Fargate のようなマネージド環境が、この custom supplier の最適なユースケースとして明記されています。
具体的な実装では、AwsSecurityCredentialsSupplier インターフェースを満たすクラスを定義し、getAwsRegion() と getAwsSecurityCredentials() を実装します。getAwsSecurityCredentials() の内部で AWS SDK のクラスを呼び出して一時認証情報を取得し、それを Google のライブラリへ返却する形をとります。
実行時に ECS task role の AWS 認証情報を取得して Google Cloud WIF に引き渡すという本質的なアプローチは、cred-config.json 方式と共通しています。違いは Google 認証ライブラリへの入力方法にあります。
-
cred-config.json方式:AWS 認証情報を環境変数に注入し、ライブラリには設定ファイルを読み込ませる。 -
custom supplier 方式:AWS 認証情報を動的に返す supplier オブジェクトを、
AwsClientの初期化時に直接渡す。
custom supplier 方式の方が、SDK の提供する正規の拡張ポイントに則って認証情報を供給する構造になります。
cred-config.json が不要になるわけではない
ここで混同しやすい点として、「custom supplier 方式を採用すれば cred-config.json が不要になるため、WIF の設定情報自体を管理しなくてよくなる」という解釈がありますが、これは正確ではありません。
以下は SDK リファレンスに示されているサンプルコードです。
import { AwsClient, AwsSecurityCredentials, AwsSecurityCredentialsSupplier, ExternalAccountSupplierContext } from 'google-auth-library';
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
import { Storage } from '@google-cloud/storage';
class AwsSupplier implements AwsSecurityCredentialsSupplier {
private readonly region: string
constructor(region: string) {
this.region = options.region;
}
async getAwsRegion(context: ExternalAccountSupplierContext): Promise<string> {
// Return the AWS region i.e. "us-east-2".
return this.region
}
async getAwsSecurityCredentials(
context: ExternalAccountSupplierContext
): Promise<AwsSecurityCredentials> {
// Retrieve the AWS credentails.
const awsCredentialsProvider = fromNodeProviderChain();
const awsCredentials = await awsCredentialsProvider();
// Parse the AWS credentials into a AWS security credentials instance and
// return them.
const awsSecurityCredentials = {
accessKeyId: awsCredentials.accessKeyId,
secretAccessKey: awsCredentials.secretAccessKey,
token: awsCredentials.sessionToken
}
return awsSecurityCredentials;
}
}
const clientOptions = {
audience: '//iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$WORKLOAD_POOL_ID/providers/$PROVIDER_ID', // Set the GCP audience.
subject_token_type: 'urn:ietf:params:aws:token-type:aws4_request', // Set the subject token type.
aws_security_credentials_supplier: new AwsSupplier("AWS_REGION") // Set the custom supplier.
service_account_impersonation_url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/$EMAIL:generateAccessToken', // Set the service account impersonation url.
}
// Create a new Auth client and use it to create service client, i.e. storage.
const authClient = new AwsClient(clientOptions);
const storage = new Storage({ authClient });
Where the audience is: //iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$WORKLOAD_POOL_ID/providers/$PROVIDER_ID
Where the following variables need to be substituted:
$PROJECT_NUMBER: The Google Cloud project number.
$WORKLOAD_POOL_ID: The workload pool ID.
$PROVIDER_ID: The provider ID.
The values for audience, service account impersonation URL, and any other builder field can also be found by generating a credential configuration file with the gcloud CLI.
コードから分かる通り、custom supplier 方式であっても audience、subject_token_type、service_account_impersonation_url といった WIF 固有のメタデータは必須です。
-
cred-config.json方式:これらの設定情報を外部の JSON ファイルに保持する。 -
custom supplier 方式:これらの設定情報を
AwsClientのclientOptionsオブジェクトに直接記述する。
すなわち、変わるのは「設定情報の保持場所」であり、必要なパラメータ自体が削減されるわけではありません。custom supplier 方式は「外部の設定ファイルへの依存を排除するコードベースの方式」と言えます。
2つの方式の比較
2つのアプローチの特徴は以下のように整理できます。
-
cred-config.json方式
Google Cloud の公式手順に近く、アーキテクチャの理解が容易です。どの Pool / Provider を経由し、どのサービスアカウントを借用するかがファイルとして明示されるため、初期検証やチーム内への説明に適しています。ただし、ECS Fargate では IMDS の差異を埋めるための環境変数へのマッピング処理がコード側に必要となります。 -
custom supplier 方式
google-auth-library が提供する正規のフックを利用して、AWS 認証情報を実行時に動的に供給します。SDK リファレンス上も、ECS / Fargate 環境は custom supplier 方式のよいユースケースとして示されているため、Node.js アプリケーションの実装としてはより自然な構造にまとまります。
なお、custom supplier は Node.js 特有の機能ではありません。Python の google-auth ライブラリにも AwsSecurityCredentialsSupplier クラスが用意されており、google.auth.aws.Credentials にサプライヤーを渡すことで、認証情報とリージョンをアプリケーションロジック側から動的に注入可能です。Python 環境においても cred-config.json の credential_source に依存しない構成が構築できます。
ただし、custom supplier 方式を導入する場合、ライブラリ側は supplier が返却した AWS 一時認証情報をキャッシュしない仕様である点に注意が必要です。必要に応じて、supplier 側でキャッシュロジックを実装する必要があります。
本番実装ではどちらを選ぶべきか
WIF の基本原理を把握し、Google IAM 側の属性マッピング(attribute mapping)やプリンシパル設定(principalSet)の整合性を検証するフェーズにおいては、構成が可視化されている cred-config.json 方式が適しています。前回の検証でも、この方式をとることでノイズの少ないシンプルな確認が可能でした。
一方で、本番環境へのデプロイを見据えた実装コードの最適化フェーズにおいては、custom AWS security credentials supplier 方式の採用を検討するのが合理的です。AWS 認証情報の解決ロジックが supplier クラスの内部にカプセル化されるため、アプリケーション全体のコードの見通しが向上します。特に、環境固有の認証設定ファイル(JSON)をコンテナイメージやボリュームに混入させたくない場合や、認証設定一式をアプリケーションコード側で一元管理したい設計要求がある場合、custom supplier 方式が適しています。
いずれの方式を選択する場合でも、Google IAM 側における Workload Identity Pool / Provider の構築、サービスアカウントへの roles/iam.workloadIdentityUser の付与、および属性連携の定義は同一のステップが必要となります。
まとめ
ECS Fargate 環境から Google Cloud Workload Identity Federation を利用する際、EC2 をターゲットとした公式ドキュメントの手順をそのまま適用しようとすると、メタデータ取得経路の差異に直面します。
前回の検証では、ADC + cred-config.json 方式を採用しつつ、タスクロールからの解決をアプリケーション側でハンドリングすることで、Node.js / Python の双方でキーレス認証を成立させました。
一方で、Node.js の google-auth-library や Python の google-auth では、ECS / Fargate などの環境に対して custom AWS security credentials supplier を用いるアプローチが提供されています。
したがって、インフラ構築の初期フェーズでは cred-config.json 方式を用いて Google IAM 側の連携を検証し、アプリケーションの洗練化フェーズで custom supplier 方式への移行を検討するというアプローチが推奨されます。
重要なのは、方式の変更によって WIF に必要な設定パラメータ自体が消失するわけではないという点です。情報を JSON ファイルに持たせるか、AwsClient の clientOptions に持たせるか。また、AWS 認証情報を環境変数経由でバインドするか、supplier 経由でインジェクションするか。
この実装パターンの違いを整理しておくことで、ECS Fargate から Google Cloud へのセキュアなキーレスアクセスを、より見通しの良いアーキテクチャで設計可能になります。
