Aller au contenu

GKE Workload Identity : lier un KSA à un GSA sans clé JSON

2 façons de s'identifier sur les services Google : les service accounts (SA) avec leur clé JSON ou Workload Identity.

La clé d'un SA est un secret qu'il faut stocker, distribuer à chaque pod qui en a besoin et rotate en cas de compromission...

Workload Identity règle ça : on lie un service account K8s (KSA) à un service account GCP (GSA) et les SDK GCP récupèrent automatiquement un token depuis la metadata API du nœud. Cette approche marche sur tous les types de nodes, y compris les Spot Nodes.

Schéma Workload Identity : Pod → KSA → GSA → services GCP

Câbler le cluster, le GSA et le KSA

2 points à activer : le cluster (workload_pool) et chaque node pool (GKE_METADATA).

Node pool obligatoire

Sans workload_metadata_config { mode = "GKE_METADATA" } sur le node pool, Workload Identity ne fonctionne pas même si le cluster est configuré.

# Cluster - activer le workload pool
resource "google_container_cluster" "main" {
  # ...
  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }
}

# Node pool - activer GKE_METADATA (déclenche un rolling update si existant)
resource "google_container_node_pool" "default" {
  # ...
  node_config {
    workload_metadata_config {
      mode = "GKE_METADATA"
    }
  }
}

# GSA et ses permissions
resource "google_service_account" "app" {
  account_id   = "my-app-sa"
  display_name = "my-app service account"
}

resource "google_project_iam_member" "app_storage" {
  project = var.project_id
  role    = "roles/storage.objectViewer"
  member  = "serviceAccount:${google_service_account.app.email}"
}

# Liaison KSA → GSA
# Le format du member est strict : PROJECT.svc.id.goog[NAMESPACE/KSA_NAME]
resource "google_service_account_iam_member" "workload_identity" {
  service_account_id = google_service_account.app.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "serviceAccount:${var.project_id}.svc.id.goog[${var.namespace}/${var.ksa_name}]"
}

# KSA annoté
resource "kubernetes_service_account" "app" {
  metadata {
    name      = "my-app"
    namespace = var.namespace
    annotations = {
      "iam.gke.io/gcp-service-account" = google_service_account.app.email
    }
  }
}

Utiliser l'identité dans le pod

Le pod ne référence que le KSA, les SDK GCP récupèrent le token tout seuls via ADC :

spec:
  serviceAccountName: my-app
  containers:
    - name: my-app
      image: my-app:latest
      # Les SDK GCP utilisent ADC automatiquement, rien d'autre à faire

Vérifier que ça fonctionne

Un pod jetable monté sur le KSA suffit à valider toute la chaîne :

kubectl run -it --rm workload-identity-test \
  --image=google/cloud-sdk:slim \
  --namespace=${K8S_NAMESPACE} \
  --overrides='{"spec":{"serviceAccountName":"'${KSA_NAME}'"}}' \
  -- bash

Dans le pod :

gcloud auth list
# ou via la metadata API directement
curl -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email"

Troubleshooting

Symptôme Cause probable
could not find default credentials Annotation KSA manquante ou mauvais SA dans le pod
403 Permission denied IAM binding manquant ou mauvais rôle sur le GSA
Token GSA incorrect Node pool sans GKE_METADATA
svc.id.goog absent de la metadata Workload Identity non activé sur le cluster

Les 2 commandes qui tranchent entre un problème d'IAM et un problème de metadata :

# Vérifier la liaison IAM
gcloud iam service-accounts get-iam-policy \
  ${GSA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com

# Debug metadata API depuis un pod
curl -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/"

Voir aussi