Workload Identity lets your GKE pods impersonate GCP service accounts, granting them granular permissions without needing to distribute google-credentials.json files.

Here’s a GKE pod trying to access a Cloud Storage bucket:

apiVersion: v1
kind: Pod
metadata:
  name: storage-reader
spec:
  containers:
  - name: reader
    image: google/cloud-sdk:latest
    command: ["/bin/bash", "-c"]
    args:
    - >
      gsutil ls gs://my-secret-bucket &&
      echo "Successfully listed bucket!"
    env:
    - name: GOOGLE_APPLICATION_CREDENTIALS
      value: "/var/run/secrets/google/application_default_credentials.json"
  serviceAccountName: my-gke-service-account # This is the Kubernetes Service Account

This pod, storage-reader, is configured to use a Kubernetes Service Account named my-gke-service-account. This isn’t the GCP Service Account directly, but rather the bridge.

To make this work, we need a few pieces in place:

  1. A GCP Service Account: This is the identity that will actually have permissions to GCP resources.
  2. A Kubernetes Service Account (KSA): This is the identity within your GKE cluster.
  3. A Workload Identity Binding: This links the KSA to the GCP Service Account.

Let’s set them up.

1. Create a GCP Service Account

First, create the GCP Service Account that your pod will impersonate. This account should have the minimum necessary permissions. For example, to list objects in a Cloud Storage bucket, it needs storage.objects.list.

gcloud iam service-accounts create my-gke-workload-identity \
    --display-name "GKE Workload Identity for Storage Access"

Now, grant the necessary role to this GCP Service Account.

# Replace 'my-project-id' with your actual project ID
gcloud projects add-iam-policy-binding my-project-id \
    --member "serviceAccount:my-gke-workload-identity@my-project-id.iam.gserviceaccount.com" \
    --role "roles/storage.objectViewer"

This command grants the roles/storage.objectViewer role to the GCP Service Account, allowing it to view objects in any bucket in the project. For finer-grained control, you’d grant this role on a specific bucket.

2. Create a Kubernetes Service Account (KSA)

Next, create the Kubernetes Service Account within your GKE cluster. This is the identity your pod will reference.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-gke-service-account # This is the KSA name used in the pod spec
  namespace: default # Or your target namespace

Apply this to your cluster:

kubectl apply -f ksa.yaml

3. Enable Workload Identity on your GKE Cluster

Workload Identity needs to be enabled on your GKE cluster. If it’s not already, you can enable it when creating a new cluster or update an existing one.

For a new cluster:

gcloud container clusters create my-cluster \
    --zone us-central1-a \
    --workload-pool my-project-id.svc.cloud.com # Important: replace my-project-id

For an existing cluster:

gcloud container clusters update my-cluster \
    --zone us-central1-a \
    --update-workload-identity-config \
    --workload-pool my-project-id.svc.cloud.com # Important: replace my-project-id

The --workload-pool value is crucial. It’s a unique identifier for your cluster’s identity pool. It typically follows the pattern YOUR_PROJECT_ID.svc.cloud.com.

4. Create the Workload Identity Binding

This is the core step that connects your KSA to your GCP Service Account. You use gcloud for this, specifying the KSA, its namespace, the GCP Service Account, and the workload pool.

gcloud iam service-accounts add-iam-policy-binding \
    my-gke-workload-identity@my-project-id.iam.gserviceaccount.com \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:my-project-id.svc.cloud.com/my-gke-service-account" \
    --project my-project-id
  • my-gke-workload-identity@my-project-id.iam.gserviceaccount.com: The GCP Service Account created in step 1.
  • roles/iam.workloadIdentityUser: This role allows the specified member (your KSA) to impersonate the GCP Service Account.
  • serviceAccount:my-project-id.svc.cloud.com/my-gke-service-account: This is the identity string for your KSA. It combines your cluster’s workload pool and the KSA’s name. Note the serviceAccount: prefix.

5. Annotate the Kubernetes Service Account

Finally, you need to tell GKE which GCP Service Account the KSA should impersonate. You do this by annotating the KSA.

kubectl annotate serviceaccount \
    my-gke-service-account \
    iam.gke.io/gcp-service-account=my-gke-workload-identity@my-project-id.iam.gserviceaccount.com \
    -n default # Use your namespace

This annotation is what tells the GKE node agent to fetch short-lived credentials for the specified GCP Service Account when a pod uses this KSA.

Deploying the Pod

Now, when you deploy the storage-reader pod defined earlier, it will automatically use the credentials of my-gke-workload-identity@my-project-id.iam.gserviceaccount.com.

kubectl apply -f pod.yaml

You should see output like:

gsutil ls gs://my-secret-bucket
# ... list of objects ...
Successfully listed bucket!

The GOOGLE_APPLICATION_CREDENTIALS environment variable is still useful because the Cloud SDK client libraries expect credentials to be at that path. The GKE node agent, when Workload Identity is enabled, injects a special metadata server at that path. When the SDK tries to fetch credentials, it queries this metadata server, which then performs the impersonation behind the scenes.

The most subtle part of this entire process is that the GKE node agent is responsible for injecting the federated credentials. It watches for pods using a Service Account that has been annotated with iam.gke.io/gcp-service-account. When it finds one, it intercepts requests to the metadata server for application default credentials and provides temporary credentials for the annotated GCP service account.

The next thing you’ll likely encounter is wanting to manage permissions at a more granular level than the whole project, which involves configuring IAM policies on specific GCP resources.

Want structured learning?

Take the full Gke course →