AWSマルチアカウント環境からのGoogle Cloudフェデレーション設計 ― AI時代に対応した社内認証基盤の構築
本文の状態
日本語全文を表示中
詳細モードで約14分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
LayerX Tech Blog
AI活用や業務自動化が進む中、AWSとGoogle Workspace/GCPを跨いだ安全なアクセスを実現するためのフェデレーション認証基盤の設計について述べている。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
LayerX Fintech事業部より、三井物産デジタル・アセットマネジメント(以下、MDM)に出向しているpiroshiです。
AIの活用や業務自動化が当たり前となった今、データや処理はプラットフォームをまたいで動くことが増えています。特に「システム基盤はAWSで動かしつつ、社内業務はGoogle Workspace前提」といった構成では、AWS上のワークロードからGoogle Cloud(以下、GCP)やGoogle WorkspaceのAPIへ安全にアクセスしたい場面が自然に出てきます。
MDMでもそのニーズが顕在化しました。具体的には、共有ドライブの構成情報を定期的に取得して監査(権限・設定の棚卸し)を自動化したいという要件があります。加えて、別のチームではGoogle Drive上のリソースにアクセスする業務ツール開発も並行して進んでいました。ここでチームごとに認証方式が増えていくと、長期鍵の配布・権限の肥大化・監査のしづらさといった問題が起きやすくなります。そこで、個別最適に寄せるのではなく、安全で監査可能な共通のアクセス方式を用意することを目標に設計を検討しました。
サービスアカウントキーを発行してAWS側に置けば動きますが、長期鍵の配布・ローテーション・棚卸しは運用負荷が高く、漏えい時の影響も大きくなります。今回はWorkload Identity Federation(WIF)を使い、静的キーを持たずに短命トークンでアクセスする構成を設計しました。
WIFを使った実装の情報は多く見つかりましたが、AWSがマルチアカウント構成の場合にどう設計すべきかについて触れているものがなかなか見つからず、さらに、GCPのベストプラクティスに沿って「WIFを専用プロジェクトに集約する」構成を採ると、具体的にどう設計・運用が変わるのかイメージが湧きにくかったです。
この記事では、「AWS Organizationsを使ったマルチアカウント環境」で、「GCP側へ出ていく認証経路(入口)を運用・統制の都合でSecurityアカウントに集約」しつつ、「ワークロード単位で識別して最小権限を保つ」ためにどう設計したかをまとめます。検証時にハマったポイントとあわせて共有できればと思います。
なお、記事ではAWS→GCPへのフェデレーションに焦点を当てています。Google Workspace APIを操作する場合のDomain-Wide Delegation(DWD)設定については、ここでは扱いません。
AWS Organizationsを使ったマルチアカウント戦略を採用している
Google側へ出ていく認証経路(入口)を、運用・統制の都合でSecurityアカウントに集約したい
そのうえで、「ワークロード単位で識別して最小権限を付与」を維持する
結論:「入口」と「権限」を分けた2段階のアクセス制御
採用した構成は以下です。まず認証フローの概要を示します。
フローチャート LR サブグラフ AWS_Org[AWS Organizations] サブグラフ Workloads[Workload Accounts] WL_A[Workload Account A] WL_B[Workload Account B] end セキュリティ [セキュリティアカウント] end サブグラフ GCP_Org[GCP Organization] サブグラフ WIF_Proj[WIF 専用プロジェクト] WIF[WIF プール/プロバイダー] end サブグラフ Service_Projs[サービスプロジェクト群] SA_A[SA / リソース A] SA_B[SA / リソース B] end end WL_A -->|1. クロスアカウント AssumeRole| セキュリティ WL_B -->|1. クロスアカウント AssumeRole| セキュリティ セキュリティ -->|2. トークン交換| WIF WIF -->|3. 名乗り出 (Impersonate)| SA_A WIF -->|3. 名乗り出 (Impersonate)| SA_B
Service Account(SA) の権限で GCP リソースにアクセスします。Google Workspace API を使う場合は、SA に Domain-Wide Delegation(DWD) を付与してユーザーとして操作する構成になります。
この構成では、認証フローの各ステップでログが取得できます。
AWS: AssumeRole
どのロールがどのロールを AssumeRole したか
GCP: トークン交換 (Token exchange)
Cloud Audit Logs(WIF プロジェクト)
attribute_condition の成否、どのフェデレーション ID がトークン交換したか
GCP: SA 名乗り出 (SA impersonation)
Cloud Audit Logs(SA のプロジェクト)
どのフェデレーション ID がどの SA を名乗り出したか
OAuth ログ、各サービス監査ログ(Drive など)
Google Workspace へのアクセス状況
WIF 専用プロジェクトと SA のプロジェクトが分かれていても、それぞれのプロジェクトで Cloud Audit Logs が記録されるため、追跡が可能です。
設計思想:2 段階のアクセス制御
attribute_condition
想定外のアカウント/ロールを入口で落とす
第 2 段階:Service Account
ワークロード単位の権限分離 (最小権限)
principalSet とは:WIF のフェデレーション ID(federated identity) を指定するための識別子です。属性に基づいて「どのフェデレーション ID に権限を付与するか」を定義します。
形式:principalSet://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{POOL_ID}/attribute.{ATTRIBUTE_NAME}/{ATTRIBUTE_VALUE}
例:principalSet://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/aws-workload-id/attribute.aws_role/arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
プールに到達できても、サービスアカウント(SA)への IAM バインディングがなければ、フェデレーション ID は impersonate できません。
ワークロードアカウントの実行ロール(IAM Role)から、セキュリティアカウント上の GCP フェデレーション用ロール(IAM Role)へクロスアカウント AssumeRole を実行します。
そのロールの AWS 認証情報を用いて、WIF が要求する署名付き GetCallerIdentity に相当する情報を生成し、Google STS へトークン交換を行います。
プロバイダーの attribute_condition
SA に対する roles/iam.workloadIdentityUser の付与
SA の権限で GCP リソース(必要に応じて DWD で Workspace API)にアクセスします。
補足:WIF が識別する「AWS 側の ID」とは
WIF が AWS 側の ID を識別する際に使用する STS の GetCallerIdentity は、以下の形式です。
arn:aws:sts::{ACCOUNT_ID}:assumed-role/{ROLE_NAME}/{SESSION_NAME}
セッション名は実行ごとに変わるため、WIF のプール側での設定(attribute_mapping)では以下のように記述します。
arn:aws:sts::{ACCOUNT_ID}:assumed-role/{ROLE_NAME}
ARN のうち、ユーザー側でコントロールできるのは IAM Role の名前です。そのため、命名規則を統一することで attribute_condition を適切に設計できます。
設計で検討した 5 つのポイント
- GCP プロジェクト構成:既存プロジェクトの利用 vs 専用プロジェクトの新設
既存プロジェクトに WIF リソースを追加する
WIF 専用プロジェクトを新規作成する
GCP のベストプラクティスでは、「専用のプロジェクトを使用して Workload Identity プールとプロバイダを管理すること」が推奨されています。
セキュリティ境界の明確化(他リソースとの分離)
WIF の管理権限(誰がプール/プロバイダーを操作できるか)を分離・集中管理できます。
一貫した attribute_mapping や attribute_condition を適用可能です。
外部からのフェデレーション経路を一箇所に集約することで、監視・監査すべきポイントが明確になり、セキュリティ上の攻撃対象領域を限定できます。
WIF を専用プロジェクトに集約し、実際のアクセス先となる SA が別のプロジェクトにあってもアクセスできるか懸念がありましたが、SA に対して roles/iam.workloadIdentityUser を付与すれば問題ありません。
- AWS IAM Role 構成:単一共有 vs ワークロード別分離
この設計では、AWS 側に 2 種類の IAM Role が登場します。
Workload Account
Lambda や ECS タスクなど、ワークロードが実行時に使用するロール。ワークロードごとに異なる。
GCP Federation 用ロール
Security Account
GCP への認証 (WIF) に使用するロール。実行ロールから AssumeRole される。
ここで検討するのは、Security Account の GCP Federation 用ロールを、全ワークロードで共有するか、ワークロードごとに分けるかです。
全ワークロードで共有する単一の GCP Federation 用ロール
ワークロードごとに個別の GCP Federation 用ロールを作成
前提として、各ワークロードが必要とする権限 (AWS 側のアクセス先リソースや、GCP 側で取得したい SA の権限) が異なる場合、実行ロールをワークロードごとに分けるのが自然です。
しかし、入口を Security Account に集約する設計では、GCP から見えるのは Security Account 側の「GCP Federation 用ロール」です (厳密には、識別できるのは STS ARN で、そのなかに IAM Role Name が含まれます)。GCP 側で利用できる属性は GetCallerIdentity 由来の情報 (account/arn/userid
よって、ワークロード単位の分離を GCP 側で実現するには、GCP Federation 用ロールもワークロードごとに分ける必要があります。
AWS(Security Account): GCP Federation 用ロール (例: workload-a)
Security Account 側(ID: 111111111111) # - SA へのアクセスを許可するワークロードの実行ロールからの AssumeRole を許可 # - AWS リソースへのアクセス権限は不要なため、信頼ポリシーのみ定義(IAM ポリシーのアタッチなし)
resource "aws_iam_role" "gcp_wif_workload_a" {
name = "gcp-wif-workload-a-role"
assume_role_policy = data.aws_iam_policy_document.trust_from_workload_a_exec.json
}
data "aws_iam_policy_document" "trust_from_workload_a_exec" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [
"arn:aws:iam::222222222222:role/workload-a-exec-role"
]
}
}
}
AWS(Workload Account A): 実行ロールに AssumeRole 権限を付与
Workload Account A 側 (ID: 222222222222) # Security Account の GCP Federation 用ロールへの AssumeRole を許可
data "aws_iam_policy_document" "allow_assume_gcp_wif" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
resources = ["arn:aws:iam::111111111111:role/gcp-wif-workload-a-role"]
}
}
resource "aws_iam_role_policy" "workload_a_exec_assume_gcp_wif" {
name = "assume-gcp-wif"
role = aws_iam_role.workload_a_exec.name
policy = data.aws_iam_policy_document.allow_assume_gcp_wif.json
}
- attribute_condition: 完全一致 vs パターンマッチ
まず、attribute_condition(属性条件)について解説します。
これは attribute_mapping(属性マッピング)の結果に対して適用される条件式です。
例えば、AWS から受け取る assertion.arn の値が
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role/session123
であった場合、attribute_mapping を用いて
attribute.aws_role に
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
という値を抽出します。
これにより、実行ごとに変わるセッション名に依存せず、IAM Role 単位での制御が可能になります。
では、attribute_condition をどのように設定するかですが、Role ARN を完全一致で列挙する方式と、プレフィックス(接頭辞)でパターンマッチングする方式があります。
完全一致で列挙する場合:
== "{workload-a の ARN}"
| == "{workload-b の ARN}"
この方法では、新しいワークロードが追加されるたびに Provider の attribute_condition を更新する必要があり、管理コストが高くなります。❌ 却下
一方、プレフィックスでパターンマッチングする方式:
startsWith("...assumed-role/gcp-wif-")
を採用した attribute_condition は、
attribute.aws_role.startsWith('arn:aws:sts::111111111111:assumed-role/gcp-wif-')
のように記述します。
- principalSet: Pool 全体許可 vs 個別 Role 指定
roles/iam.workloadIdentityUser の指定方法には粒度の違いがあり、Pool 全体を許可するか、特定の属性値(assumed-role ARN)で絞り込むかを選べます。
Pool 全体を許可する場合:
principalSet://.../{pool}/*
特定の属性値で絞り込む場合:
principalSet://.../attribute.aws_role/{assumed-role ARN}
プール全体での許可では、Workload A 用の GCP フェデレーションロールが Workload B のサービスアカウントをインパーソネートできてしまい、ワークロード間の分離が壊れてしまいます。個別指定であれば、「この AWS ロールはこのサービスアカウントのみ」という 1:1 の対応を強制できます。
GCP 側の Terraform(プール/プロバイダー/IAM バインディング)
Workload Identity Pool / Provider
WIF 専用プロジェクト
resource "google_iam_workload_identity_pool" "aws_pool" {
project = "my-wif-project"
workload_identity_pool_id = "aws-workload-id"
display_name = "AWS Workload Identity Pool"
description = "Federation from AWS Security Account"
}
resource "google_iam_workload_identity_pool_provider" "aws_provider" {
project = "my-wif-project"
workload_identity_pool_id = google_iam_workload_identity_pool.aws_pool.workload_identity_pool_id
workload_identity_pool_provider_id = "security-account"
display_name = "AWS Security Account Provider"
aws {
account_id = "111111111111" # セキュリティアカウント ID
}
attribute_mapping = {
"google.subject" = "assertion.arn" # セッション名を除去して assumed-role ARN に変換
"attribute.aws_role" = "assertion.arn.contains('assumed-role') ? assertion.arn.extract('{account_arn}assumed-role/') + 'assumed-role/' + assertion.arn.extract('assumed-role/{role_name}/') : assertion.arn"
}
# 命名規則に合致する assumed-role のみ受け入れ
attribute_condition = "attribute.aws_role.startsWith('arn:aws:sts::111111111111:assumed-role/gcp-wif-')"
}
Service Account と IAM Binding(workload-a 例)
サービスプロジェクト側で作る想定
resource "google_service_account" "workload_a" {
project = "my-service-project"
account_id = "workload-a"
display_name = "Service Account for workload-a"
}
重要: principalSet は PROJECT_NUMBER を使う(PROJECT_ID ではない)
resource "google_service_account_iam_binding" "workload_a_wif_user" {
service_account_id = google_service_account.workload_a.name
role = "roles/iam.workloadIdentityUser"
members = [
"principalSet://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/aws-workload-id/attribute.aws_role/arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role"
]
}
新ワークロード追加時の担当タスク
AWS(Security): GCP Federation 用 IAM Role を追加
GCP: ワークロード専用 SA (Service Account) を作成し、必要最小限の権限を付与
GCP: SA に roles/iam.workloadIdentityUser を付与
AWS(Workload): 実行ロールに GCP Federation 用ロールへの AssumeRole 権限を付与
管理者側の GCP 作業 (SA 作成 + IAM Binding) は以下のような Terraform で管理できます:
ワークロード専用 SA
resource "google_service_account" "workload_a" {
project = "my-wif-project"
account_id = "workload-a"
display_name = "Service Account for workload-a"
}
IAM Binding: GCP フェデレーション用ロール → SA
aws_iam_role.gcp_wif_workload_a は Security Account 側で定義した GCP フェデレーション用ロール
resource "google_service_account_iam_binding" "workload_a_wif_user" {
service_account_id = google_service_account.workload_a.name
role = "roles/iam.workloadIdentityUser"
members = [
# IAM Role ARN (arn:aws:iam::...) を STS assumed-role ARN 形式に変換
"principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.aws_pool.name}/attribute.aws_role/arn:aws:sts::${data.aws_caller_identity.security.account_id}:assumed-role/${aws_iam_role.gcp_wif_workload_a.name}"
]
}
命名規則に従う限り、Provider の attribute_condition
補足:管理権限もセットで設計する
WIF(Workload Identity Federation)の設計とあわせて、以下のようなガードレールも検討しておくと安心です。
サービスアカウントキー作成禁止(組織ポリシー)を有効化して、長期鍵が増えないようにする
WIF の Pool/Provider を勝手に作られないように、権限や組織ポリシー(カスタム制約)で縛る
この設計を検証する中で苦労した点を紹介します。
1) コンポーネントが多く、トラブルシューティングが難しい
WIF は AWS と GCP を跨ぎ、IAM Role、STS(Security Token Service)、Provider、Pool、SA など関連するコンポーネントが多いです。問題が発生した場合は、推測ではなくログをもとに原因を切り分けていくのが確実です。
AWS: AssumeRole
どのロールがどのロールを AssumeRole したか
GCP: トークン交換(Token exchange)
Cloud Audit Logs (iam.googleapis.com)
attribute_condition の成否など
GCP: SA インパーソネーション(SA impersonation)
Cloud Audit Logs (iam.googleapis.com)
どのフェデレーション ID がどの SA をインパーソネートしたか
DWD など Workspace 側の監査
2) AssumeRole 時の環境変数の扱いに注意(Lambda など)
例えば Lambda で Google Cloud クライアントライブラリ(Python の google-auth)
google-auth-library
AWS_ACCESS_KEY_ID
Lambda では起動時に実行ロールの認証情報が環境変数にセットされています。AssumeRole 後の認証情報で環境変数を上書きすると、以降の AWS SDK 呼び出しも AssumeRole 後の認証情報で動作してしまいます。つまり、元の実行ロールに付与していた権限が使えなくなります。
今回は try-finally
const prev = { accessKeyId: process.env.AWS_ACCESS_KEY_ID, /* ... */ }; try { process.env.AWS_ACCESS_KEY_ID = assumedCreds.AccessKeyId; // ... WIF 処理 (GCP クライアントライブラリ経由) } finally { process.env.AWS_ACCESS_KEY_ID = prev.accessKeyId; // 必ず元に戻す }
入口 (Security Account) と権限 (GCP 側の SA) を分け、WIF を専用プロジェクトに集約すると、マルチアカウントでも運用しやすい
Provider は「受け入れ条件」、IAM Binding は「実際の権限」と役割分担すると設計が整理できる
attribute_mapping
ログ (CloudTrail / Cloud Audit Logs / Workspace 監査ログ) を前提に、切り分け可能な形にしておく
Workload Identity Federation | Google Cloud
Configure Workload Identity Federation with AWS or Azure | Google Cloud
Download credential configuration and grant access | Google Cloud(principalSet の形式と使用方法)
Domain-Wide Delegation | Google Workspace Admin Help
Restricting service account usage | Google Cloud
Custom organization policies for WIF | Google Cloud
サービス アカウント認証用のロール | Google Cloud(roles/iam.workloadIdentityUser
許可ポリシーについて | Google Cloud(IAM Binding の説明)
今回は、マルチアカウントな AWS 環境から GCP/Google Workspace に安全にアクセスするために、Workload Identity Federation をどう設計したかをまとめました。運用はこ
原文を表示
LayerX Fintech 事業部から、三井物産デジタル・アセットマネジメント(以下、MDM) に出向している piroshi です。
AI 活用や業務自動化が当たり前になってきた今、データや処理はプラットフォームをまたいで動くことが増えています。特に「システム基盤はAWSで動かしつつ、社内業務は Google Workspace 前提」といった構成では、AWS 上のワークロードから Google Cloud(以下、GCP) や Google Workspace の API へ安全にアクセスしたい場面が自然に出てきます。
MDM でもそのニーズが顕在化しました。具体的には、共有ドライブの構成情報を定期的に取得して監査(権限・設定の棚卸し)を自動化したい、という要件があります。加えて、別のチームでは Google Drive 上のリソースにアクセスする業務ツール開発も並行して進んでいました。ここでチームごとに認証方式が増えていくと、長期鍵の配布・権限の肥大化・監査のしづらさといった問題が起きやすくなります。そこで、個別最適に寄せるのではなく、安全で監査可能な共通のアクセス方式を用意することを目標に設計を検討しました。
サービスアカウントキーを発行して AWS 側に置けば動きますが、長期鍵の配布・ローテーション・棚卸しは運用負荷が高く、漏えい時の影響も大きくなります。今回は Workload Identity Federation(WIF) を使い、静的キーを持たずに短命トークンでアクセスする構成を設計しました。
WIF を使った実装の情報は多く見つかりましたが、AWS がマルチアカウント構成の場合にどう設計すべきかについて触れているものがなかなか見つからず、さらに、GCP のベストプラクティスに沿って「WIF を専用プロジェクトに集約する」構成を採ると、具体的にどう設計・運用が変わるのかイメージが湧きにくかったです。
この記事では、「AWS Organizations を使ったマルチアカウント環境」で、「GCP 側へ出ていく認証経路(入口)を運用・統制の都合で Security アカウントに集約」しつつ、「ワークロード単位で識別して最小権限を保つ」ためにどう設計したかをまとめます。検証時にハマったポイントとあわせて共有できればと思います。
なお、記事では AWS→GCP へのフェデレーションに焦点を当てています。Google Workspace API を操作する場合の Domain-Wide Delegation(DWD)設定については、ここでは扱いません。
AWS Organizations を使ったマルチアカウント戦略を採用している
Google 側へ出ていく認証経路(入口)を、運用・統制の都合で Security アカウントに集約したい
そのうえで、「ワークロード単位で識別して最小権限を付与」を維持する
結論: 「入口」と「権限」を分けた 2 段階のアクセス制御
採用した構成は以下です。まず認証フローの概要を示します。
flowchart LR subgraph AWS_Org[AWS Organizations] subgraph Workloads[Workload Accounts] WL_A[Workload Account A] WL_B[Workload Account B] end Security[Security Account] end subgraph GCP_Org[GCP Organization] subgraph WIF_Proj[WIF 専用プロジェクト] WIF[WIF Pool/Provider] end subgraph Service_Projs[サービスプロジェクト群] SA_A[SA / Resources A] SA_B[SA / Resources B] end end WL_A -->|1. Cross-Account AssumeRole| Security WL_B -->|1. Cross-Account AssumeRole| Security Security -->|2. Token Exchange| WIF WIF -->|3. Impersonate| SA_A WIF -->|3. Impersonate| SA_B
Service Account(SA)の権限で GCP リソースにアクセスします。Google Workspace API を使う場合は、SA に Domain-Wide Delegation(DWD)を付与してユーザーとして操作する構成になります。
この構成では、認証フローの各ステップでログが取得できます。
AWS: AssumeRole
どのロールがどのロールを AssumeRole したか
GCP: Token exchange
Cloud Audit Logs(WIF プロジェクト)
attribute_condition の成否、どのフェデレーション ID がトークン交換したか
GCP: SA impersonation
Cloud Audit Logs(SA のプロジェクト)
どのフェデレーション ID がどの SA を impersonate したか
OAuth ログ、各サービス監査ログ(Drive 等)
Google Workspace へのアクセス状況
WIF 専用プロジェクトと SA のプロジェクトが分かれていても、それぞれのプロジェクトで Cloud Audit Logs が記録されるため、追跡が可能です。
設計思想: 2段階のアクセス制御
attribute_condition
想定外のアカウント/ロールを入口で落とす
第2段階: Service Account
ワークロード単位の権限分離(最小権限)
principalSet とは: WIF のフェデレーション ID(federated identity)を指定するための識別子です。属性に基づいて「どのフェデレーション ID に権限を付与するか」を定義します。
形式: principalSet://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{POOL_ID}/attribute.{ATTRIBUTE_NAME}/{ATTRIBUTE_VALUE}
例: principalSet://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/aws-workload-id/attribute.aws_role/arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
Pool に到達できても、SA への IAM Binding がなければフェデレーション ID は impersonate できません。
Workload Account の実行ロール(IAM Role)から、Security Account 上の GCP Federation 用ロール(IAM Role)にクロスアカウント AssumeRole
そのロールの AWS 認証情報で、WIF が必要とする署名付き GetCallerIdentity 相当の情報を作り、Google STS へトークン交換
Provider の attribute_condition
SA に対する roles/iam.workloadIdentityUser
SA の権限で GCP リソース (必要なら DWD で Workspace API) にアクセス
補足: WIF が識別する「AWS 側の ID」とは
WIF が AWS 側の ID を識別する際に使うのは、STS の GetCallerIdentity
arn:aws:sts::{ACCOUNT_ID}:assumed-role/{ROLE_NAME}/{SESSION_NAME}
セッション名は実行ごとに変わるため、WIF の Pool 側の設定(attribute_mapping
arn:aws:sts::{ACCOUNT_ID}:assumed-role/{ROLE_NAME}
ARN のうち、ユーザー側でコントロールできるのは IAM Role の名前です。そのため、命名規則を統一することで attribute_condition
設計で検討した 5 つのポイント
- GCP プロジェクト構成: 既存のプロジェクトを利用 vs 専用プロジェクトを新設
既存プロジェクトに WIF リソースを追加
WIF 専用プロジェクトを新規作成
GCP のベストプラクティスで「専用のプロジェクトを使用して Workload Identity プールとプロバイダを管理する」ことが推奨されている
セキュリティ境界の明確化(他リソースと分離)
WIF の管理権限(誰が Pool/Provider を触れるか)を分離・集中管理できる
一貫した attribute_mapping や attribute_condition を適用できる
外部からのフェデレーション経路を一箇所に集約することで、監視・監査すべきポイントが明確になり、セキュリティ上の攻撃対象領域を限定できる
WIF を専用プロジェクトに集約し、実際のアクセス先となる SA が別のプロジェクトにあってもアクセスできるのかが懸念でしたが、SA に対して roles/iam.workloadIdentityUser
- AWS IAM Role 構成: 単一共有 vs ワークロード別分離
この設計では、AWS 側に 2 種類の IAM Role が登場します。
Workload Account
Lambda や ECS タスクなど、ワークロードが実行時に使用するロール。ワークロードごとに異なる。
GCP Federation 用ロール
Security Account
GCP への認証(WIF)に使用するロール。実行ロールから AssumeRole される。
ここで検討するのは、Security Account の GCP Federation 用ロールを、全ワークロードで共有するか、ワークロードごとに分けるかです。
全ワークロードで共有する単一の GCP Federation 用ロール
ワークロードごとに個別の GCP Federation 用ロールを作成
前提として、各ワークロードが必要とする権限(AWS 側のアクセス先リソースや、GCP 側で取得したい SA の権限)が異なる場合、実行ロールをワークロードごとに分けるのが自然です。
しかし、入口を Security Account に集約する設計では、GCP から見えるのは Security Account 側の「GCP Federation 用ロール」です(厳密には、識別できるのは STS ARN で、そのなかに IAM Role Name が含まれます)。GCP 側で利用できる属性は GetCallerIdentity 由来の情報(account/arn/userid
よって、ワークロード単位の分離を GCP 側で実現するには、GCP Federation 用ロールもワークロードごとに分ける必要があります。
AWS(Security Account): GCP Federation 用ロール(例: workload-a)
Security Account 側(ID: 111111111111) # - SA へのアクセスを許可するワークロードの実行ロールからの AssumeRole を許可 # - AWS リソースへのアクセス権限は不要なため、信頼ポリシーのみ定義(IAM ポリシーのアタッチなし) resource "aws_iam_role" "gcp_wif_workload_a" { name = "gcp-wif-workload-a-role" assume_role_policy = data.aws_iam_policy_document.trust_from_workload_a_exec.json } data "aws_iam_policy_document" "trust_from_workload_a_exec" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals { type = "AWS" identifiers = [ "arn:aws:iam::222222222222:role/workload-a-exec-role" ] } } }
AWS(Workload Account A): 実行ロールに AssumeRole 権限を付与
Workload Account A 側 (ID: 222222222222) # Security Account の GCP Federation 用ロールへの AssumeRole を許可 data "aws_iam_policy_document" "allow_assume_gcp_wif" { statement { effect = "Allow" actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/gcp-wif-workload-a-role"] } } resource "aws_iam_role_policy" "workload_a_exec_assume_gcp_wif" { name = "assume-gcp-wif" role = aws_iam_role.workload_a_exec.name policy = data.aws_iam_policy_document.allow_assume_gcp_wif.json }
- attribute_condition: 完全一致 vs パターンマッチ
まず、attribute_condition
attribute_mapping
attribute_condition
条件式の中では attribute_mapping
attribute_mapping
例えば、AWS から受け取る assertion.arn
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role/session123
attribute_mapping
attribute.aws_role
arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role
これにより、実行ごとに変わるセッション名に依存せず、IAM Role 単位での制御が可能になります。
では、attribute_condition
Role ARN を完全一致で列挙
`== "{workload-a の ARN}"
| == "{workload-b の ARN}"` | ❌ 却下
prefix でパターンマッチ
startsWith("...assumed-role/gcp-wif-")
完全一致で列挙する方法では、ワークロード追加のたびに Provider の attribute_condition
attribute_condition
採用した attribute_condition
attribute.aws_role.startsWith('arn:aws:sts::111111111111:assumed-role/gcp-wif-')
- principalSet: Pool 全体許可 vs 個別 Role 指定
roles/iam.workloadIdentityUser
指定方法には粒度の違いがあり、Pool 全体を許可するか、特定の属性値(assumed-role ARN)で絞り込むかを選べます。
principalSet://.../{pool}/*
principalSet://.../attribute.aws_role/{assumed-role ARN}
Pool 全体許可では、Workload A 用の GCP Federation ロールが Workload B の SA を impersonate できてしまい、ワークロード間の分離が壊れます。個別指定なら「この AWS ロールはこの SA だけ」という 1:1 の対応を強制できます。
GCP 側 Terraform(Pool / Provider / IAM Binding)
Workload Identity Pool / Provider
WIF 専用プロジェクト resource "google_iam_workload_identity_pool" "aws_pool" { project = "my-wif-project" workload_identity_pool_id = "aws-workload-id" display_name = "AWS Workload Identity Pool" description = "Federation from AWS Security Account" } resource "google_iam_workload_identity_pool_provider" "aws_provider" { project = "my-wif-project" workload_identity_pool_id = google_iam_workload_identity_pool.aws_pool.workload_identity_pool_id workload_identity_pool_provider_id = "security-account" display_name = "AWS Security Account Provider" aws { account_id = "111111111111" # Security Account ID } attribute_mapping = { "google.subject" = "assertion.arn" # セッション名を除去して assumed-role ARN に変換 "attribute.aws_role" = "assertion.arn.contains('assumed-role') ? assertion.arn.extract('{account_arn}assumed-role/') + 'assumed-role/' + assertion.arn.extract('assumed-role/{role_name}/') : assertion.arn" } # 命名規則に合致する assumed-role のみ受け入れ attribute_condition = "attribute.aws_role.startsWith('arn:aws:sts::111111111111:assumed-role/gcp-wif-')" }
Service Account と IAM Binding(workload-a 例)
サービスプロジェクト側で作る想定 resource "google_service_account" "workload_a" { project = "my-service-project" account_id = "workload-a" display_name = "Service Account for workload-a" } # 重要: principalSet は PROJECT_NUMBER を使う(PROJECT_ID ではない) resource "google_service_account_iam_binding" "workload_a_wif_user" { service_account_id = google_service_account.workload_a.name role = "roles/iam.workloadIdentityUser" members = [ "principalSet://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/aws-workload-id/attribute.aws_role/arn:aws:sts::111111111111:assumed-role/gcp-wif-workload-a-role" ] }
新ワークロード追加時の担当タスク
AWS(Security): GCP Federation 用 IAM Role を追加
GCP: ワークロード専用 SA を作成し、必要最小限の権限を付与
GCP: SA に roles/iam.workloadIdentityUser
AWS(Workload): 実行ロールに GCP Federation 用ロールへの AssumeRole 権限を付与
管理者側の GCP 作業(SA 作成 + IAM Binding)は以下のような Terraform で管理できます:
ワークロード専用 SA resource "google_service_account" "workload_a" { project = "my-wif-project" account_id = "workload-a" display_name = "Service Account for workload-a" } # IAM Binding: GCP Federation 用ロール → SA # aws_iam_role.gcp_wif_workload_a は Security Account 側で定義した GCP Federation 用ロール resource "google_service_account_iam_binding" "workload_a_wif_user" { service_account_id = google_service_account.workload_a.name role = "roles/iam.workloadIdentityUser" members = [ # IAM Role ARN (arn:aws:iam::...) を STS assumed-role ARN 形式に変換 "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.aws_pool.name}/attribute.aws_role/arn:aws:sts::${data.aws_caller_identity.security.account_id}:assumed-role/${aws_iam_role.gcp_wif_workload_a.name}" ] }
命名規則に従う限り、Provider の attribute_condition
補足: 管理権限もセットで設計する
WIF の設計とあわせて、以下のようなガードレールも検討しておくと安心です。
サービスアカウントキー作成禁止(組織ポリシー)を有効化して、長期鍵が増えないようにする
WIF の Pool/Provider を勝手に作られないように、権限や組織ポリシー(カスタム制約)で縛る
この設計を検証する中で苦労した点を紹介します。
1) コンポーネントが多く、トラブルシューティングが難しい
WIF は AWS と GCP を跨ぎ、IAM Role, STS, Provider, Pool, SA など関連するコンポーネントが多いです。問題が発生した場合は、推測ではなくログをもとに原因を切り分けていくのが確実です。
AWS: AssumeRole
どのロールがどのロールを AssumeRole したか
GCP: Token exchange
Cloud Audit Logs(iam.googleapis.com)
attribute_condition の成否など
GCP: SA impersonation
Cloud Audit Logs(iam.googleapis.com)
どのフェデレーション ID がどの SA を impersonate したか
DWD など Workspace 側の監査
2) AssumeRole 時の環境変数の扱いに注意(Lambda など)
例えば Lambda で Google Cloud クライアントライブラリ(Python の google-auth
google-auth-library
AWS_ACCESS_KEY_ID
Lambda では起動時に実行ロールの認証情報が環境変数にセットされています。AssumeRole 後の認証情報で環境変数を上書きすると、以降の AWS SDK 呼び出しも AssumeRole 後の認証情報で動作してしまいます。つまり、元の実行ロールに付与していた権限が使えなくなります。
今回は try-finally
const prev = { accessKeyId: process.env.AWS_ACCESS_KEY_ID, /* ... */ }; try { process.env.AWS_ACCESS_KEY_ID = assumedCreds.AccessKeyId; // ... WIF 処理(GCP クライアントライブラリ経由) } finally { process.env.AWS_ACCESS_KEY_ID = prev.accessKeyId; // 必ず元に戻す }
入口(Security Account)と権限(GCP 側の SA)を分け、WIF を専用プロジェクトに集約すると、マルチアカウントでも運用しやすい
Provider は「受け入れ条件」、IAM Binding は「実際の権限」と役割分担すると設計が整理できる
attribute_mapping
ログ(CloudTrail / Cloud Audit Logs / Workspace 監査ログ)を前提に、切り分け可能な形にしておく
Workload Identity Federation | Google Cloud
Configure Workload Identity Federation with AWS or Azure | Google Cloud
Download credential configuration and grant access | Google Cloud(principalSet の形式と使用方法)
Domain-Wide Delegation | Google Workspace Admin Help
Restricting service account usage | Google Cloud
Custom organization policies for WIF | Google Cloud
サービス アカウント認証用のロール | Google Cloud(roles/iam.workloadIdentityUser
許可ポリシーについて | Google Cloud(IAM Binding の説明)
今回は、マルチアカウントな AWS 環境から GCP/Google Workspace に安全にアクセスするために、Workload Identity Federation をどう設計したかをまとめました。運用はこ
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み