鍵の管理は「過去の遺物」だ:GCP Workload Identityで実現するゼロトラストな権限管理
現場のエンジニア諸君、お疲れ様。今日もどこかのサーバーで、誰かが「JSONキーファイル」をリポジトリに誤ってコミットしているかもしれないな。
君たちがGCP上でGKEを運用しているなら、今すぐ古いやり方を捨てろ。かつてのようにサービスアカウントの秘密鍵をJSONファイルとしてダウンロードし、それをKubernetesのSecretに保存する……なんてのは、現代のセキュリティ基準では「脆弱性を作っているのと同義」だ。
今日は、GKEにおけるクラウドネイティブな権限管理の決定版、「Workload Identity」の本質と、攻撃者が狙う盲点、そして今日から使える実装の作法を解説する。
—
なぜ「鍵ファイル」は死ぬべきなのか?(攻撃者の視点)
攻撃者が標的とするのは、常に「静的なクレデンシャル」だ。
もし君たちがJSONキーファイルをPodにマウントしている場合、攻撃者は以下のルートでシステムを乗っ取る。
1. LFIやRCE: アプリケーションの脆弱性(Path Traversal等)を突き、Pod内のファイルシステムにアクセス。
2. クレデンシャルの抽出: マウントされたJSONファイルを読み取り、サービスアカウントの権限を盗む。
3. 横展開: 盗んだ権限でGCPのAPIを叩き、Cloud Storageのバケットを全公開にしたり、BigQueryのデータを外部へ転送する。
鍵ファイルは「盗まれたら終わり」という、あまりにも脆い防御ラインなんだ。
—
Workload Identityの仕組み:信頼の根源を「Pod」へ
Workload Identityは、K8sの「ServiceAccount」とGCPの「IAMサービスアカウント」を、GCPのメタデータサーバー経由で紐付ける技術だ。
これを使うと、Pod内部には一切の鍵ファイルが存在しない。代わりに、GCPが発行する一時的なトークンがメタデータサーバーを通じて提供される。万が一、Podがコンプロマイズされても、トークンの有効期限は短く、キーファイルという物理的な「盗める資産」が存在しないため、攻撃者は権限昇格の起点を見失うことになる。
—
実装編:コピペで動くセキュアな構成
理論はいい。現場ですぐに使える設定を見せよう。
1. Terraformでの紐付け(インフラ構成)
まずはGCP側の準備だ。特定のPod(K8sサービスアカウント)だけに、特定のIAM権限を与える。
IAMサービスアカウントの作成
resource “google_service_account” “app_sa” {
account_id = “my-app-sa”
display_name = “Application Service Account”
}
Workload Identityの紐付け(IAMポリシー)
resource “google_service_account_iam_member” “workload_identity_binding” {
service_account_id = google_service_account.app_sa.name
role = “roles/iam.workloadIdentityUser”
member = “serviceAccount:my-project.svc.id.goog[my-namespace/my-k8s-sa]”
}
2. K8s側の設定(アノテーション)
PodがどのIAMサービスアカウントを使うかを紐付ける。これが「魔法の呪文」だ。
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-k8s-sa
namespace: my-namespace
annotations:
# ここが重要:IAMサービスアカウントを指定する
iam.gke.io/gcp-service-account: my-app-sa@my-project.iam.gserviceaccount.com
3. アプリケーションコード(Python例)
Google Cloudのクライアントライブラリは賢い。「Application Default Credentials (ADC)」を利用すれば、コード側で特別な認証情報を書く必要は一切ない。
from google.cloud import storage
Workload Identityが有効なら、この一行だけで自動的にIAM権限が適用される
鍵ファイルパスの指定は不要!
storage_client = storage.Client()
def list_blobs(bucket_name):
# 認証情報を意識せず、権限チェックを通過したAPIコールだけが成功する
blobs = storage_client.list_blobs(bucket_name)
for blob in blobs:
print(blob.name)
—
現場のチーフとしてのアドバイス:ここを外すな
最後に、インシデント現場を見てきた人間からのアドバイスだ。どれだけ技術を導入しても、運用がズボラなら意味がない。
- 最小権限の原則(PoCの罠): 「とりあえずEditorロールを付けておこう」は自殺行為だ。必要なバケットだけにアクセス権を絞る「IAM Conditions」を併用せよ。
- メタデータサーバーへのアクセス制限: GKEのPodからアクセスできるメタデータサーバー(`169.254.169.254`)は、必要に応じてネットワークポリシーで制限を検討しろ。意図しない通信を防ぐ強力な壁になる。
- 監査ログの監視: Cloud Audit Logsを有効にし、誰がいつそのサービスアカウントを使っているかを監視せよ。異常なAPIリクエストは、攻撃の予兆だ。
「便利だから」という理由で古い手法を使い続けるのは、リスクを先送りしているに過ぎない。Workload Identityは、君たちのインフラを強固にし、夜ぐっすり眠るための必須ツールだ。
さあ、コードをリライトして、セキュアな環境へ移行しよう。何か詰まったら、また聞きに来い。現場からは以上だ。

コメント