Skip to main content

Workload Identity Federation

Workload Identity Federation lets a workload outside Google Cloud exchange its own token for short-lived Google credentials, so no service account key exists. Every GitHub Actions workflow of the GCP platform authenticates this way, as the service account of its repository.

What it does​

A workload identity pool holds trust in external identities. A provider in the pool names an OIDC (OpenID Connect) issuer, maps the claims of its tokens to attributes with an attribute mapping, and rejects any token that fails its attribute condition, a CEL expression over the claims. The Security Token Service exchanges an accepted token for a federated token, and a principal holding roles/iam.workloadIdentityUser on a service account may then act as that service account. The same mechanism serves Kubernetes: Workload Identity Federation for GKE makes a Kubernetes service account an IAM principal.

How BuiltForProd uses it​

The workload-identity unit (environments/core/auto/global) creates the pool acme-github in acme-core-auto with one provider, github, for the issuer https://token.actions.githubusercontent.com. The organization policy iam.workloadIdentityPoolProviders allows no other issuer anywhere in the organization.

GCP/acme-gcp-platform-baseline/modules/workload-identity/main.tf (lines 51-73)
resource "google_iam_workload_identity_pool_provider" "github" {
#checkov:skip=CKV_GCP_125: the attribute condition is built in locals (repository owner check plus the per-repository ref and environment clauses), which checkov cannot evaluate
project = var.project_id
workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
workload_identity_pool_provider_id = "github"
display_name = "GitHub OIDC"
description = "GitHub Actions OIDC tokens of ${var.github_org}"

attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
"attribute.repository_owner" = "assertion.repository_owner"
"attribute.ref" = "assertion.ref"
"attribute.environment" = "assertion.environment"
"attribute.event_name" = "assertion.event_name"
"attribute.actor" = "assertion.actor"
}
attribute_condition = local.attribute_condition

oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
}

The attribute condition always requires repository_owner to be your GitHub organization (your-github-org), and adds one clause per restricted repository:

RepositoryService accountExtra condition on its tokens
acme-gcp-platform-baselinesa-acme-baseline-cimain branch, the prod Environment, or a pull request
acme-gcp-blueprint-webapp-infrasa-acme-webapp-infra-cinone
acme-gcp-blueprint-etl-infrasa-acme-etl-infra-cinone
acme-gcp-blueprint-webapp-codesa-acme-webapp-code-cinone
acme-gcp-blueprint-etl-codesa-acme-etl-code-cinone
acme-gcp-blueprint-secretssa-acme-secrets-syncera GitHub Environment run: sandbox, dev, staging or prod

The CI accounts live in acme-core-auto; the syncer lives in acme-core-security. Each account trusts only its own repository (principalSet://.../attribute.repository/your-github-org/<repository>). Restrictions on refs and Environments sit in the provider's condition because an IAM condition on the workloadIdentityUser binding cannot read token claims.

The chain. A CI account holds no rights in the stage projects itself. It impersonates the target project's deployer sa-acme-terraform-deployer, on which it holds Token Creator, so a job ends up with Owner of one project only: GitHub token, then sa-acme-<key>-ci, then the deployer. The Baseline's 15 organization-scoped units run as sa-acme-baseline-ci itself, which holds organization and folder roles and never Owner at the organization. Service accounts describes both identities.

Pods. The Web App Blueprint binds roles straight to the application's Kubernetes service account as a principal of the stage's pool acme-plat-<stage>.svc.id.goog, with no Google service account in between, and the application reaches Firestore and Memorystore as that principal. The external-dns releases and the External Secrets Operator receive their access the same way.

Terms you will see​

TermMeaning
Poolacme-github, the trust container in acme-core-auto.
Providergithub, the trust in the GitHub Actions OIDC issuer.
Attribute conditionThe CEL expression every token must satisfy.
principalSetA member that matches every token with a given attribute, such as one repository.
Security Token ServiceThe API (sts.googleapis.com) that exchanges external tokens, enabled in every project.

Where to read more​