Un pod a besoin d'accéder à une API GCP — Cloud Storage, Pub/Sub, Secret Manager. Historiquement, la réponse était simple : exporter une clé JSON de service account, la monter comme Secret Kubernetes, et laisser le pod s'authentifier avec. Cette réponse est aussi le problème.

Le problème des clés de service account statiques

Une clé JSON exportée n'est pas une preuve d'identité liée au pod — c'est un fichier. Un fichier que n'importe qui disposant d'un accès kubectl peut lire (kubectl get secret -o yaml suffit), copier, et réutiliser depuis n'importe où : un poste local, un autre cluster, une machine hors du périmètre GCP.

Pire, ces clés sont statiques et longue durée. Elles restent valides jusqu'à révocation manuelle, sans lien avec le cycle de vie du pod qui les utilise. Un pod supprimé depuis des mois peut avoir laissé une clé toujours active quelque part.

Une clé de service account exportée n'est plus une preuve d'identité du pod — c'est un fichier que n'importe qui peut copier.

Workload Identity : lier une identité Kubernetes à une identité GCP

Workload Identity supprime le fichier de l'équation. Au lieu de distribuer une clé, il crée un lien IAM direct entre un Kubernetes Service Account (KSA) et un Google Service Account (GSA). Le pod s'authentifie avec son KSA ; GKE échange cette identité contre un jeton GCP de courte durée, émis dynamiquement via le metadata server du nœud — jamais stocké sur disque, jamais exporté.

Configuration en 5 étapes

1. Activer Workload Identity sur le cluster, avec le pool au format PROJECT_ID.svc.id.goog :

gcloud container clusters update my-cluster \
  --workload-pool=my-gcp-project.svc.id.goog

2. Créer le Google Service Account (GSA) avec les rôles IAM nécessaires :

gcloud iam service-accounts create orders-gsa \
  --display-name="Orders Service Account"

3. Créer le binding IAM reliant le KSA au GSA via roles/iam.workloadIdentityUser :

gcloud iam service-accounts add-iam-policy-binding \
  orders-gsa@my-gcp-project.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:my-gcp-project.svc.id.goog[production/orders-ksa]"

4. Annoter le Kubernetes Service Account avec la référence au GSA :

kubectl annotate serviceaccount orders-ksa \
  -n production \
  iam.gke.io/gcp-service-account=orders-gsa@my-gcp-project.iam.gserviceaccount.com

5. Déployer le pod avec ce KSA — aucun Secret, aucune clé montée :

apiVersion: v1
kind: ServiceAccount
metadata:
  name: orders-ksa
  namespace: production
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-processor
  namespace: production
spec:
  template:
    spec:
      serviceAccountName: orders-ksa
      containers:
        - name: orders-processor
          image: gcr.io/my-gcp-project/orders-processor:latest

Le détail de sécurité qui change tout

Le binding IAM ne cible pas juste un GSA — il cible une paire précise namespace + nom de KSA. Un pod dans le namespace staging avec un service account nommé orders-ksa n'hérite d'aucun droit, même si le nom est identique à celui de production, tant qu'aucun binding explicite ne l'autorise. L'isolation est portée par Kubernetes lui-même, pas par une convention de nommage ou une discipline d'équipe.

Ce que ça change concrètement

  • Zéro fichier de clé statique : rien à exporter, rien à monter, rien à exfiltrer.
  • Jetons éphémères : liés au cycle de vie du pod, pas à un fichier qui survit indéfiniment.
  • Binding précis par namespace : pas de sur-privilège accidentel entre environnements.
  • Moins de rotation manuelle : pas de clé à faire tourner, pas d'audit de fichiers exportés.
  • Compatible Workload Identity Federation : même logique extensible à des charges hors GKE.

La clé de service account exportée a longtemps été la solution par défaut. Sur GKE, elle est aujourd'hui l'exception à justifier — pas la règle à appliquer.