【Workload Identity】 GKE で Google Service Account を利用する正しいやり方


TL;DR

  • GKE から Google Service Account (GSA) を利用するのに GSA のアカウントキーを発行して Secret を作成してはいけない

  • Kubernetes Service Account (KSA) と GSA を紐づける Workload Identity を利用して認証を行うべき

Why?

以下は公式ドキュメントからの抜粋。
アカウントキーを発行してGCPのプロダクトを利用するのには、

  1. キーの漏洩リスク
  2. キーの更新(デフォルトの有効期限は10年)

という Secret を手動で管理、保護する手間がありました。

注: Workload Identity は、GKE 内から Google Cloud サービスにアクセスする場合におすすめの方法です。Workload Identity を使用すると、Kubernetes サービス アカウントを Google サービス アカウントとして機能するよう構成でき、Secret を手動で管理、保護する必要がなくなります。

Workload Identity とは

一言で言うと、「kubernetes の Service Account GCP の Serivce Account を紐づけ(Binding)られるようにする仕組み」です。アカウントキーでの認証方法とWorkload Identity での認証方法を図で比較します。

アカウントキーでの認証方法

Workload Identity での認証方法

利用手順

大別すると以下のようになります。
詳細は公式ドキュメントを参照してください。

  1. クラスタで Workload Identity を有効化
  2. GSA を作成し、KSA との IAM policy Binding を作成。
  3. KSA を作成し、GSA へのアノテーションを追加。
  4. Pod で KSA を指定

1. クラスタで Workload Identity を有効化

  • 新しいクラスタで Workload Identity を有効にする
$ gcloud container clusters create cluster-name \
  --release-channel regular \
  --workload-pool=project-id.svc.id.goog
  • 既存のクラスタで Workload Identity を有効にする
$ gcloud container clusters update cluster-name \
  --workload-pool=project-id.svc.id.goog

2. GSA を作成し、KSA との IAM policy Binding を作成

$ gcloud iam service-accounts create gsa-name
$ gcloud iam service-accounts add-iam-policy-binding \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:project-id.svc.id.goog[k8s-namespace/ksa-name]" \
  [email protected]

Terraform
main.tf

data "google_project" "project" {
}

locals {
  annotations = formatlist(
    "serviceAccount:${data.google_project.project.project_id}.svc.id.goog[%s]",
    var.k8s_service_accounts,
  )
}

data "google_service_account" "serviceaccount" {
  account_id = var.gcp_service_account_id
}

resource "google_service_account_iam_binding" "wi_iam_binding" {
  service_account_id = data.google_service_account.serviceaccount.name
  role               = "roles/iam.workloadIdentityUser"
  members            = local.annotations
}
variables.tf
variable "gcp_service_account_id" {
  type        = string
  default     = ""
  description = "GCP service account ID (before `@`)"
}

variable "k8s_service_accounts" {
  type        = list(string)
  default     = []
  description = "Kubernetes service accounts in the format of `$ns/$sa_name`"
}

3. KSA を作成し、GSA へのアノテーションを追加

$ kubectl create serviceaccount --namespace k8s-namespace ksa-name
$ kubectl annotate serviceaccount \
  --namespace k8s-namespace \
  ksa-name \
  iam.gke.io/[email protected]

Manifest
serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: $KSA_NAME
  namespace: $K8S_NAMESPACE
  annotations:
    iam.gke.io/gcp-service-account: "$GSA_NAME@$PROJECT_ID.iam.gserviceaccount.com"

4. Pod で KSA を指定

deployment.yaml
apiVersion: apps/v1
kind: Deployment
# ...
spec:
  template:
    spec:
      serviceAccountName: $KSA_NAME
      # ...
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true"