【IAM】AWSとGoogle Cloudのクラウド間認証の方法について整理してみる
データ事業本部の川中子(かわなご)です。
規模の大きい組織になると、複数のクラウド環境が混在しているケースも多いと思います。
そのような環境でのクラウド間連携では、認証方法が重要なポイントになります。
今回は、AWSとGoogle Cloud間のセキュアな認証方法について整理してみました。
クラウド間の認証方法
クラウド認証で最も簡素な方法は、アクセス先でアクセスキーを発行し、
そのアクセスキーをアクセス元に譲渡することで認証を行う方法です。
しかしこの方法にはいくつかの課題があります。
- アクセスキーの漏洩リスク
- キーローテーションなどの運用管理コスト
これらの理由から、アクセスキー譲渡によるクラウド間認証ではなく、
フェデレーションで短期クレデンシャルを取得する方法が推奨されています。
この方法はAWSではAssumeRoleWithWebIdentity、
Google CloudではWorkload Identity Federationと呼ばれています。
1. Google CloudからAWSにアクセス
1.1. 概要
AssumeRoleWithWebIdentityは、外部IdPのJWTトークンでIAMロールを引き受ける仕組みです。
Google CloudのJWTトークンを使って、AWS側のIAMロールを引き受けることができます。
1.2. 基本的な実装方法
まずはGoogle CloudからAWSリソースにアクセスする手順を説明していきます。
前提として、Google Cloud側で利用するサービスアカウントは作成されている想定です。
1.2.1. AWS側でIAMロールを作成する
Google Cloud側に利用させたいIAMロールをAWS側で作成します。
ポイントになるのはカスタム信頼ポリシーの設定です。
Google Cloudのサービスアカウントに対してアシュームを許可する場合、
設定するカスタムポリシーの例は以下のようになります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "accounts.google.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"accounts.google.com:oaud": "<audience値>",
"accounts.google.com:sub": "<サービスアカウントの一意ID>"
}
}
}
]
}
今回のIdPはGoogle Cloudなので、Federatedはaccounts.google.comで固定です。
accounts.google.com:oaudは任意の文字列を設定することができますが、
Google Cloud側でJWTトークンを取得する際に、同じ文字列を渡す必要があります。
accounts.google.com:subにはアクセスを許可するサービスアカウントのIDを設定します。
このIDは以下コマンドでSERVICE_ACCOUNT_EMAILを指定することで確認できます。
gcloud iam service-accounts describe <SERVICE_ACCOUNT_EMAIL> --format="value(uniqueId)"
# 出力例
123456789012345678901
1.2.2. Google Cloud側でJWTトークンを取得する
Google Cloud側のサービスアカウントを利用して、JWTトークンを取得します。
この時のaudiencesは、前段で作成したAWS IAMロールのカスタム信頼ポリシーの、
accounts.google.com:oaudに設定した値と一致させる必要があります。
例としてPythonで実装する場合には以下のようになります。
import urllib.request
import urllib.parse
def get_google_identity_token():
try:
metadata_server = 'http://metadata.google.internal/computeMetadata/v1/'
token_request_url = metadata_server + 'instance/service-accounts/default/identity'
token_request_headers = {'Metadata-Flavor': 'Google'}
params = {'audience': '<audience値>', 'format': 'full', 'include_email': 'true'}
url = f"{token_request_url}?{urllib.parse.urlencode(params)}"
req = urllib.request.Request(url, headers=token_request_headers)
with urllib.request.urlopen(req) as response:
identity_token = response.read().decode('utf-8')
return identity_token
except Exception as e:
raise Exception(f"JWT取得エラー: {e}")
1.2.3. JWTトークンでAWS IAMロールを引き受ける
取得したJWTトークンを利用してAssumeRoleWithWebIdentityを実行します。
import boto3
def assume_aws_role(config):
jwt_token = get_google_identity_token()
sts_client = boto3.client('sts')
response = sts_client.assume_role_with_web_identity(
RoleArn='arn:aws:iam::<AWSアカウントID>:role/<ロール名>',
RoleSessionName='<任意のセッション名>',
WebIdentityToken=jwt_token
)
return response['Credentials']
上記で取得したクレデンシャルを利用することで、AWSのリソースにアクセスできます。
Cloud ShellなどからCLIを利用する方法については、
以下のブログでとても詳細に説明されています。
1.3. 複数プリンシパルの登録
複数のプリンシパルからのアクセスを許可しておきたい場合は、
以下のようにaccounts.google.com:subをリストで記述することで実現可能です。
"Condition": {
"StringEquals": {
"accounts.google.com:oaud": "<audience値>",
"accounts.google.com:sub": [
"<サービスアカウントの一意ID>",
"<サービスアカウントの一意ID>",
"<サービスアカウントの一意ID>"
]
}
}
その場合の構成イメージは以下のようになります。

アクセスを許可したい対象を変更したい場合には、
対象のIAMロールの信頼ポリシーを修正することで対応が可能です。
2. AWSからGoogle Cloudにアクセス
2.1. 概要
次にGoogle Cloud側のWorkload Identity Federationは、
外部IdPのクレデンシャルを使ってGoogle Cloudにアクセスする機能です。
2.2. 基本的な実装方法
AWSからGoogle Cloudリソースにアクセスする手順を説明していきます。
前提として、Google Cloud側で利用するサービスアカウントと、
AWS側で利用するIAMロールは既に作成されている想定です。
AWS側で使用していたAssumeRoleWithWebIdentity向けの設定とは異なり、
Google Cloudのサービスアカウントの作成時に特別な設定は不要です。
2.2.1. Workload Identity Poolを作成する
まずは外部IDプロバイダーからのIDをグループ化するコンテナとして、
Google Cloud上でWorkload Identity Poolを作成します。
gcloud iam workload-identity-pools create <PoolのID> \
--location="global" \
--display-name="<Poolの表示名>" \
--description="<Poolの説明>"
2.2.2. Workload Identity Providerを作成する
作成したPool内にAWS用のProviderを作成します。
以下では特定のアカウントIDと、アシュームされたロールであることを条件にしています。
gcloud iam workload-identity-pools providers create-aws <ProviderのID> \
--location="global" \
--workload-identity-pool="<PoolのID>" \
--account-id="<AWSアカウントID>" \
--display-name="<Providerの表示名>" \
--description="<Providerの説明>" \
--attribute-mapping="google.subject=assertion.arn,attribute.aws_account=assertion.account,attribute.aws_role=assertion.arn.extract('assumed-role/{role}/')" \
--attribute-condition="assertion.arn.startsWith('arn:aws:sts::<AWSアカウントID>:assumed-role/')"
作成したPoolとProviderは、コンソールで以下のように確認できます。
複数アカウントに対して設定する場合は、プロバイダーごとに設定を分けるとよさそうです。

2.2.3. サービスアカウントとWorkload Identityを紐付ける
対象のサービスアカウントに、Workload Identity Poolからの借用を許可します。
gcloud iam service-accounts add-iam-policy-binding \
<サービスアカウントのアドレス> \
--role="roles/iam.workloadIdentityUser" \
--member="principal://iam.googleapis.com/projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<Pool名>/attribute.aws_role/<ロール名>"
このとき--memberで指定する<ロール名>の部分は、
AWS側で使用するIAMロールの名前と一致させておく必要があります。
2.2.4. 認証情報の構成ファイルをダウンロードする
AWS側で使用する認証情報の構成ファイルを生成します。
gcloud iam workload-identity-pools create-cred-config \
projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/<PoolのID>/providers/<providerのID> \
--service-account=<サービスアカウントのアドレス> \
--aws \
--output-file=<FILEPATH.json>
構成ファイルは以下のコンソール画面からもダウンロード可能です。

構成ファイルのダウンロード方法の詳細は以下のドキュメントを参照してください。
2.3. 認証構成ファイルの管理
認証情報の構成ファイル自体には秘密鍵の情報は記載されていないので、
この構成ファイル自体が漏洩しても直接的なリスクにはなりません。
なので特定のLambdaやEC2で使用する場合は、直接ファイルを配置することもできますし、
複数サービスで共通利用する場合はParameter Storeなどで管理することも可能です。
以下は例として、LambdaからParameter Storeに登録した構成ファイルを参照して、
Google Cloudの対象プロジェクトのバケット一覧を取得するスクリプトです。
なお以下のLambdaではWorkload Identityと紐づけしたIAMロールを使用しています。
import json
import boto3
from google.auth import aws
from google.cloud import storage
def lambda_handler(event, context):
# Parameter Storeから認証構成を取得
ssm = boto3.client('ssm')
response = ssm.get_parameter(Name='<パラメータのpath>')
config = json.loads(response['Parameter']['Value'])
# AWS用の認証情報を生成
credentials = aws.Credentials.from_info(config)
# GCSクライアント作成
client = storage.Client(
credentials=credentials,
project="<プロジェクトID>"
)
# バケット一覧を取得
buckets = [bucket.name for bucket in client.list_buckets()]
return {
'statusCode': 200,
'body': json.dumps({'buckets': buckets})
}
以下のブログではEC2からGeminiを利用する場合の設定について説明されています。
こちらも併せてご覧ください。
2.4. 複数プリンシパルの登録
複数のプリンシパルに対してアクセスを許可しておきたい場合は、
前述の通りWorkload Identity Providerをその分登録しておく必要があります。
この場合の構成イメージは以下のようになります。

新しいプリンシパルにアクセスを許可したい場合は、Providerを追加すれば対応できます。
さいごに
今回は、AWSとGoogle Cloud間のセキュアな認証方法について整理してみました。
フェデレーションを利用することで、アクセスキーの譲渡なしで認証を実現できます。
一度この設定を行えば、連携先が増えた場合でも効率的に、
そして何よりセキュアに認証を実現できるのは大きなメリットだと思います。
今回の記事が少しでも参考になれば幸いです。
最後まで記事を閲覧いただきありがとうございました。






