はじめに
前回は GCP のリソースを Terraform で管理できるようにしました。今回は、GCP のリソースを Kubernetes 上の Pod から、サービスアカウントキーを使わずに利用できるようにしていきます。
動機
GCP のリソースにアクセスする方法で、最もシンプルなのはサービスアカウントキーを発行する方法です。
しかし、サービスアカウントキーは次のようなデメリットがあります。
- 漏洩のリスクがある
- 有効期限切れになる前に再発行する必要があり、管理が大変
そこで、GCP の Workload Identity 連携を使って、Kubernetes 上の Pod から GCP のリソースにアクセスできるようにしようと思いました。
実装
Kubernetes の情報を取得
Kubernetes に接続して、issuer URL を確認します。
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer
クラスタの JSON Web Key Set (JWKS) を確認します。
kubectl get --raw /openid/v1/jwks > cluster-jwks.json
ダウンロードして、terraform プロジェクトのディレクトリに配置します。
GCP の設定
基本的には公式ドキュメントに従って設定します。
まず、まだ作成していない場合は Workload Identity Pool を作成します。
resource "google_iam_workload_identity_pool" "default" {
workload_identity_pool_id = "pool"
}
先程ダウンロードした JWKS を terraform から使えるようにします。
data "local_file" "cluster_jwks" {
filename = "${path.module}/cluster-jwks.json"
}
Kubernetes の Workload Identity Pool Provider を作成します。
resource "google_iam_workload_identity_pool_provider" "home_kubernetes" {
workload_identity_pool_id = google_iam_workload_identity_pool.default.workload_identity_pool_id.workload_identity_pool_id
workload_identity_pool_provider_id = "home-kubernetes"
display_name = "Home Kubernetes"
description = "for Kubernetes in the home"
attribute_mapping = {
"google.subject" = "assertion.sub"
}
oidc {
issuer_uri = "{issuer URL}"
jwks_json = data.local_file.cluster_jwks.content
}
}
使用するサービスアカウントを作成します。
resource "google_service_account" "sa" {
account_id = "sa"
create_ignore_already_exists = true
}
与えたいロールを作成します。Workload Identity 連携をするためには、roles/iam.workloadIdentityUser
が必要です。
resource "google_project_iam_member" "clubroom_storage_object_user" {
project = var.project
member = "serviceAccount:${google_service_account.clubroom.email}"
role = "roles/storage.objectUser"
}
resource "google_project_iam_member" "clubroom_service_account_token_creator" {
project = var.project
member = "serviceAccount:${google_service_account.clubroom.email}"
role = "roles/iam.workloadIdentityUser"
}
最後に、サービスアカウントを Kubernetes から使用できるようにします。
resource "google_service_account_iam_member" "ksa_workload_identity_user" {
service_account_id = google_service_account.clubroom.id
role = "roles/iam.workloadIdentityUser"
member = "principal://iam.googleapis.com/projects/${var.project_number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.default.workload_identity_pool_id}/subject/system:serviceaccount:default:{ksa-name}"
}
Kubernetes 側の設定
まず、次のような JSON を作成します。
{
"universe_domain": "googleapis.com",
"type": "external_account",
"audience": "//iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/providers/{provider_id}",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": {
"file": "/var/run/service-account/token",
"format": {
"type": "text"
}
},
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{gsa_email}:generateAccessToken"
}
この JSON をもとに ConfigMap を作成します。
# kustomization.yaml
- name: workload-identity-credential-configuration
files:
- gcp-credential-configuration.json
Deployment に以下の 2 つの Volume を作成します。
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
audience: https://iam.googleapis.com/projects/{project_number}/locations/global/workloadIdentityPools/{pool_id}/providers/{provider_id}
expirationSeconds: 3600
path: token
- name: workload-identity-credential-configuration
configMap:
name: workload-identity-credential-configuration
Volume をマウントします。
volumeMounts:
- name: token
mountPath: /var/run/service-account
readOnly: true
- name: workload-identity-credential-configuration
mountPath: /etc/workload-identity
readOnly: true
環境変数で作成した設定へのパスを指定します。
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /etc/workload-identity/gcp-credential-configuration.json
以上で、Kubernetes 上の Pod から GCP のリソースにアクセスできるようになります。
まとめ
今回は、GCP のリソースにアクセスする方法として、Workload Identity 連携を使う方法を紹介しました。
サービスアカウントキーを発行せずに GCP を使えるようになって、かなり便利になったと感じています。
今後は Kubernetes 側の設定を自動でやってくれる Custom Controller なんかを作ってみたいなと感じています。