workload identity for local kubernetes clusters

no secrets anywhere

SEAN K.H. LIAO

workload identity for local kubernetes clusters

no secrets anywhere

workload identity in any k8s cluster

One of the things I like about GCP is that every workload always has an identity (GCP service account) attached, and the GCP client libraries can just magically pick up the identity and make authenticated calls.

For Google managed kubernetes (GKE), there's a layer of indirection, as you (typically) don't want everything in your cluster running on the same identity. This is Workload Identity: your Kubernetes service accounts are annotated with iam.gke.io/gcp-service-account=GSA_NAME@GSA_PROJECT.iam.gserviceaccount.com and a GKE metadata agent running as a daemonset on your nodes serves the GCP metadata endpoint with the appropriate info. The GCP service account needs to grant permissions to GSA_PROJECT.svc.id.goog[NAMESPACE/KSA_NAME], but otherwise, the workload itself doesn't need to change.

For Kubernetes running anywhere else, there's Workload Identity Federation to trust AWS, Azure, or any OIDC provider, including the one built into Kubernetes (Service Account Issuer Discovery). Your K8s api endpoint doesn't even need to be exposed to the world. Setup is slightly more involved (workload identity pool + provider), and the workload definitions need to change: you'll need to pass GOOGLE_APPLICATION_CREDENTIALS, and mount both a token and the generated config. The client libraries start with the env, read the file, then read the token. One hiccup was that depending on the library, you may need to explicitly pass the google project.

generated config:

 1{
 2  "type": "external_account",
 3  "audience": "//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID",
 4  "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
 5  "token_url": "https://sts.googleapis.com/v1/token",
 6  "credential_source": {
 7    "file": "/var/run/service-account/token",
 8    "format": {
 9      "type": "text"
10    }
11  },
12  "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/GSA_NAME@GSA_PROJECT.iam.gserviceaccount.com:generateAccessToken"
13}

application spec:

 1spec:
 2  template:
 3    spec:
 4      containers:
 5        - env:
 6            - name: GOOGLE_APPLICATION_CREDENTIALS
 7              value: /var/run/service-account/gcp-workload-identity.json
 8          volumeMounts:
 9            - name: token
10              mountPath: "/var/run/service-account"
11              readOnly: true
12      serviceAccountName: my-app
13      volumes:
14        - name: token
15          projected:
16            sources:
17              - serviceAccountToken:
18                  audience: https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID
19                  expirationSeconds: 3600
20                  path: token
21              - configMap:
22                  name: gcp-workload-identity