Cloud Security Wire
AWS Azure GCP RSS
GCP Misconfiguration

GCP Workload Identity Federation: Attack Paths When the Trust Rules Are Too Broad

Workload Identity Federation replaces GCP service account key files with keyless, short-lived tokens. But misconfigured pool trust rules — particularly those federating with GitHub Actions or multi-tenant identity providers — create privilege escalation paths that attackers are actively exploiting. Here is what the attacks look like and how to audit your federation configuration.

By Cloud Security Wire · ·
#gcp#workload-identity-federation#oidc#github-actions#privilege-escalation#iam#keyless-auth#cicd

Workload Identity Federation (WIF) is the correct modern approach to GCP authentication for external workloads. It eliminates the need to create and distribute service account key files by allowing external identities — GitHub Actions workflows, AWS EC2 instance profiles, Azure managed identities, on-premises Kubernetes workloads — to exchange their native credentials for short-lived GCP access tokens. No static secrets to rotate, no key files to accidentally commit.

The problem is in the trust rules. When those rules are too broad, WIF creates a privilege escalation path that is substantially worse than the service account keys it replaced: an attacker who compromises any identity trusted by an overly permissive pool gains access to all GCP permissions bound to the attached service account — and unlike key files, there may be no secret to rotate because the trust rule is configuration, not a credential.

How WIF Trust Chains Work

A Workload Identity Pool is a GCP resource that defines a set of external identity providers (IdPs) trusted to exchange tokens for GCP access. Each pool can have multiple provider configurations. A provider maps to one external IdP — a GitHub Actions OIDC endpoint, an AWS STS endpoint, a corporate Kubernetes cluster.

The attack surface lives in attribute conditions on provider configurations and service account bindings. When a workload requests a GCP token via WIF, the federation service evaluates:

  1. Does the presented token come from a trusted IdP (the provider’s issuer URL)?
  2. Does the token’s attributes satisfy the attribute condition on the provider?
  3. Is there a service account binding that grants this workload identity roles/iam.workloadIdentityUser on the target service account?

If the attribute condition is overly broad — or absent — any identity that can obtain a token from the trusted IdP can satisfy step 2.

The GitHub Actions Over-Trust Pattern

GitHub’s OIDC endpoint (token.actions.githubusercontent.com) is a multi-tenant IdP: it issues tokens to any GitHub Actions workflow on the entire platform. If a WIF provider trusts this endpoint without an attribute condition scoped to a specific repository, owner, or workflow, the following is possible:

  1. Attacker creates a GitHub account and a repository.
  2. Attacker writes a GitHub Actions workflow that requests a GitHub OIDC token and exchanges it for a GCP token using the target organisation’s WIF pool.
  3. If the pool provider has no attribute condition (or a condition that does not check google.subject, attribute.repository, or attribute.repository_owner), the federation succeeds.
  4. Attacker now holds a short-lived GCP access token with the permissions of whatever service account is bound to the pool identity.

This attack requires knowledge of the target’s WIF pool name and project ID — both of which may be visible in public CI/CD configuration, Terraform files, or GitHub Actions workflow files checked into public repositories.

Auditing Your WIF Configuration

List all Workload Identity Pools and Providers

# List pools in your project
gcloud iam workload-identity-pools list \
  --project=YOUR_PROJECT_ID \
  --location=global \
  --format="table(name, state, displayName)"

# For each pool, list providers
gcloud iam workload-identity-pools providers list \
  --workload-identity-pool=POOL_ID \
  --project=YOUR_PROJECT_ID \
  --location=global \
  --format="json"

Inspect Provider Attribute Conditions

gcloud iam workload-identity-pools providers describe PROVIDER_ID \
  --workload-identity-pool=POOL_ID \
  --project=YOUR_PROJECT_ID \
  --location=global \
  --format="json(attributeCondition, attributeMapping, oidc)"

Look for providers where attributeCondition is null or empty. For GitHub OIDC providers specifically, a missing condition means any GitHub workflow can exchange a token against this pool.

Find Service Account Bindings

# Find which service accounts grant workloadIdentityUser to your pool
gcloud iam service-accounts get-iam-policy SA_EMAIL \
  --project=YOUR_PROJECT_ID \
  --format=json | jq '.bindings[] | select(.role == "roles/iam.workloadIdentityUser")'

This reveals the blast radius: which service accounts are reachable from an overly broad pool, and what GCP permissions those accounts hold.

Hardening: Attribute Conditions That Actually Constrain Access

GitHub Actions — Minimum Viable Restriction

# Require tokens to come from a specific repository AND branch
gcloud iam workload-identity-pools providers update-oidc PROVIDER_ID \
  --workload-identity-pool=POOL_ID \
  --project=YOUR_PROJECT_ID \
  --location=global \
  --attribute-condition="assertion.repository == 'your-org/your-repo' && assertion.ref == 'refs/heads/main'"

For production deployments, also require an Actions environment:

--attribute-condition="assertion.repository == 'your-org/your-repo' && assertion.environment == 'production'"

Attribute Mapping for Fine-Grained Conditions

Define a mapping so GCP sees the GitHub claim fields you want to filter on:

gcloud iam workload-identity-pools providers update-oidc PROVIDER_ID \
  --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner,attribute.ref=assertion.ref,attribute.environment=assertion.environment" \
  --workload-identity-pool=POOL_ID \
  --project=YOUR_PROJECT_ID \
  --location=global

Then reference these in your condition:

attribute.repository_owner == 'your-org' && attribute.repository == 'your-org/your-repo'

Multi-Provider Pool Risk

If your pool has providers for multiple external IdPs (GitHub, AWS, and a Kubernetes cluster, for example), a service account bound to the pool’s member principal (principalSet://...) is accessible from all of them. Compromise of any one federated identity provider reaches the same GCP service account.

The fix: separate pools per workload type, with separate service account bindings scoped to each pool’s specific principal set. Do not bind a high-privilege service account to a pool that contains a broadly trusted provider.

Common Misconfigurations Found in the Wild

MisconfigurationImpactDetection Method
GitHub OIDC provider with no attributeConditionAny GitHub Actions workflow can impersonate the bound SAproviders describe — null attributeCondition
Multi-tenant provider trusted for entire org rather than specific repoAny repo in the org can escalateCheck attribute.repository_owner only vs. full repo path
High-privilege SA (e.g. roles/owner) bound to a broadly trusted poolFull project compromise from external identityIAM policy audit on SA
Pool created for testing, not deleted, still has SA bindingsOrphan attack surfaceList all pools including deleted state
google.subject mapped but no attribute condition checking itSubject condition bypassed if IdP allows subject manipulationReview mapping + condition for logical gaps

Monitoring and Alerting

GCP logs WIF token exchanges in the IAM service under the activity log. Alert on unexpected patterns:

# Find WIF token issuance events in Cloud Logging
gcloud logging read \
  'protoPayload.methodName="google.iam.credentials.v1.IAMCredentials.GenerateIdToken"
   OR protoPayload.methodName="ExchangeToken"' \
  --project=YOUR_PROJECT_ID \
  --format="json" \
  --freshness=1h

Specific conditions worth alerting on:

  • WIF token exchanges from source IPs in unexpected geographies (GitHub Actions runners have documented IP ranges — exchanges outside these indicate a non-GitHub source using your pool)
  • Token exchanges for service accounts with elevated privileges (roles/owner, roles/editor, custom roles with sensitive permissions) from any workload pool
  • Token exchanges from Entra ID or AWS identities where the corresponding workload does not have a known CI/CD job running

WIF is the right architecture for keyless GCP access. The security model holds when attribute conditions are specific. The failure mode is treating a multi-tenant IdP like GitHub as a single trusted entity rather than a platform containing millions of untrusted repositories alongside your own.

← All Analysis Subscribe via RSS