概要
Workload Identity
GKE Pod内のコードがGCP APIを呼ぶとき、キーファイル不要・有効期限1時間・自動ローテーションでKSA→GSAのトークン交換が自動で行われる。
Secret Manager
外部SaaSのOAuth2クレデンシャルをGCPで一元管理。Workload Identity経由で取得することでK8s Secretの平文保存リスクを排除。
Terraform管理
GSA・IAMバインディング・Secret Manager定義をコード化。手動作業によるローテーション漏れや設定ミスをなくす。
最小権限原則
GSAに付与する権限は secretmanager.secretAccessor(特定のシークレットのみ)に絞る。BigQuery等は別途分離して管理。
問題
セキュリティレビューで指摘: 「サービスアカウントキーのローテーションが手動で行われており、漏洩リスクがある。Workload Identity Federationへの移行を検討すること」
現在の構成
| 項目 | 内容 |
|---|---|
| 実行環境 | GKE Autopilot上のArgo Workflows(毎日9:00キャンペーンメール配信) |
| コンテナ | PythonコンテナがBigQueryからユーザー取得 → 外部SaaS APIへメール配信 |
| 現在の認証 | サービスアカウントキー(JSON)をKubernetes Secretに格納 |
| 外部SaaS認証 | OAuth2.0 Client Credentials Grant(client_id / client_secret) |
制約・前提条件
- GKEクラスタはGKE Autopilot(Workload Identity有効化済み)
- Terraformでインフラ管理(既存のTFコードに追記)
- Pythonコンテナ内では
google-authライブラリを使用中 - コンテナのコード変更は最小限にしたい
期待する回答形式: 文章(問題点の特定)+ Terraformコード + Kubernetes Manifestコード + Pythonコード変更箇所
ヒント(段階的開示)
ヒント1 — 方向性
「サービスアカウントキーJSON」の何が問題か整理しよう。キーが漏洩したらどうなるか、ローテーションが手動だと何が困るかを考えると、解決方向が見えてくる。
ヒント2 — アプローチ
GKE Workload Identityを使うと、KubernetesのServiceAccountとGCPのServiceAccountを紐付けられる。
Pod内のコードは
GOOGLE_APPLICATION_CREDENTIALS を設定しなくてもGCPリソースにアクセスできる(メタデータサーバー経由)。
ただし今回の外部SaaS APIはGCPのリソースではないため、直接Workload Identityで認証できない点に注意。
ヒント3 — 典型設計パターン
外部SaaS APIの
client_secret はSecret Manager(GCP)に格納し、Workload IdentityでGSAに secretmanager.secretAccessor 権限を付与してコンテナから取得する設計が典型パターン。
# Terraform骨格
resource "google_service_account_iam_member" "workload_identity" {
service_account_id = google_service_account.campaign_job.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[${var.namespace}/${var.ksa_name}]"
}
問題点の特定
| # | 問題点 | リスク | 対策 |
|---|---|---|---|
| 1 | サービスアカウントキーJSON(長期認証情報)のリスク | キーが漏洩すると失効まで悪用され続ける。手動ローテーションは失念・遅延リスク | Workload Identityで短命トークン(1時間・自動更新)に移行 |
| 2 | K8s Secretの暗号化問題 | デフォルトのK8s SecretはetcdにBase64エンコードで保存(暗号化なし) | Secret Managerで一元管理し、K8s Secretから機密情報を排除 |
| 3 | 外部SaaS API認証情報の管理 | client_secret をK8s Secretに入れているため上記リスクが直撃する |
Secret Managerで管理 + Workload Identity経由で取得 |
アーキテクチャ図 — 改善後の認証フロー
模範解答
# GSA(GCP Service Account)の作成
resource "google_service_account" "campaign_job" {
account_id = "campaign-job"
display_name = "Campaign Job Service Account"
project = var.project_id
}
# Secret Manager にシークレットを定義
resource "google_secret_manager_secret" "saas_client_secret" {
project = var.project_id
secret_id = "saas-client-secret"
replication {
auto {}
}
}
# Secret Managerへのアクセス権限付与
resource "google_secret_manager_secret_iam_member" "campaign_job_secret_access" {
project = var.project_id
secret_id = google_secret_manager_secret.saas_client_secret.secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.campaign_job.email}"
}
# Workload Identity バインディング
# KSA: campaign-job-ksa(namespace: mops)← GSA に紐付け
resource "google_service_account_iam_member" "workload_identity" {
service_account_id = google_service_account.campaign_job.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:${var.project_id}.svc.id.goog[mops/campaign-job-ksa]"
}
apiVersion: v1
kind: ServiceAccount
metadata:
name: campaign-job-ksa
namespace: mops
annotations:
# GKE Workload Identity: GSAと紐付け
iam.gke.io/gcp-service-account: campaign-job@${PROJECT_ID}.iam.gserviceaccount.com
---
# Argo Workflow テンプレート側でServiceAccountを指定
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: campaign-mailer
namespace: mops
spec:
serviceAccountName: campaign-job-ksa # KSA を指定
templates:
- name: send-campaign
container:
image: asia-northeast1-docker.pkg.dev/${PROJECT_ID}/mops/campaign-job:latest
# GOOGLE_APPLICATION_CREDENTIALS の設定不要(Workload Identity経由)
env:
- name: GCP_PROJECT_ID
value: "${PROJECT_ID}"
- name: SAAS_CLIENT_ID
value: "your-client-id" # client_idは非機密なのでenvでOK
# client_secret はコード内でSecret Managerから取得
# before: K8s SecretからJSONキーを読む(廃止)
# import json, os
# creds = json.loads(os.environ["SAAS_CLIENT_SECRET_JSON"])
# after: Secret ManagerからOAuth2クレデンシャルを取得
from google.cloud import secretmanager
import requests
def get_saas_client_secret(project_id: str, secret_name: str = "saas-client-secret") -> str:
"""Secret ManagerからSaaS APIのclient_secretを取得する。
Workload Identityにより、サービスアカウントキーなしで認証される。
Args:
project_id: GCPプロジェクトID
secret_name: Secret Manager上のシークレット名
Returns:
client_secretの文字列
"""
client = secretmanager.SecretManagerServiceClient()
name = f"projects/{project_id}/secrets/{secret_name}/versions/latest"
response = client.access_secret_version(request={"name": name})
return response.payload.data.decode("utf-8")
def get_saas_access_token(client_id: str, client_secret: str, token_url: str) -> str:
"""OAuth2.0 Client Credentials GrantでSaaS APIのアクセストークンを取得する。"""
resp = requests.post(
token_url,
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
},
timeout=10,
)
resp.raise_for_status()
return resp.json()["access_token"]
ポイント解説
1
Workload Identity の仕組み
GKE Pod内のコードがGCP APIを呼ぶとき、メタデータサーバー(169.254.169.254)経由でKSA→GSAのトークン交換が自動で行われる。キーファイル不要・有効期限1時間・自動ローテーション。
GKE Pod内のコードがGCP APIを呼ぶとき、メタデータサーバー(169.254.169.254)経由でKSA→GSAのトークン交換が自動で行われる。キーファイル不要・有効期限1時間・自動ローテーション。
2
外部APIはWorkload Identityで直接認証できない
Workload IdentityはGCPのIAMと統合されたサービス専用。外部SaaSのOAuth2認証には使えないため、「Secret Managerで秘密情報を管理し、Workload Identity経由で取得する」という2段階設計が定石。
Workload IdentityはGCPのIAMと統合されたサービス専用。外部SaaSのOAuth2認証には使えないため、「Secret Managerで秘密情報を管理し、Workload Identity経由で取得する」という2段階設計が定石。
3
最小権限原則
GSAに付与する権限は
GSAに付与する権限は
secretmanager.secretAccessor(特定のシークレットのみ)に絞る。BigQueryアクセスが必要なら別途 bigquery.dataViewer を付与し、権限を分離して管理する。
実務への応用
MOpsチームのArgo Workflowsジョブ
複数の外部SaaS(メール配信・プッシュ通知・SMS)と連携することが多い。それぞれの client_secret をSecret Managerで一元管理し、Workload Identityで取得する設計にすると、キーローテーション時もSecret Managerの値を更新するだけでよく、K8s Secretの手動更新やPod再起動が不要になる。
次のステップ
発展問題: Secret Managerのシークレットに自動ローテーション(Cloud Functions trigger)を設定し、SaaS側のOAuth2クレデンシャルを定期更新する仕組みを設計する
- 参考: GKE公式「Workload Identity Federation for GKE」
- 参考: Secret Manager公式「シークレットのローテーション」
今日のまとめ
GKEでのサービスアカウントキーはWorkload Identityで廃止し、外部SaaS認証情報はSecret Managerで管理・Workload Identity経由で取得するのが2026年時点のベストプラクティス。
Workload Identity(短命トークン)+ Secret Manager(集中管理)+ Terraform(IaC)の組み合わせで、手動ローテーションリスクを完全に排除できる。
Workload Identity(短命トークン)+ Secret Manager(集中管理)+ Terraform(IaC)の組み合わせで、手動ローテーションリスクを完全に排除できる。