How do workloads and CI jobs access cloud APIs without static keys?
Kubernetes workloads and CI jobs access cloud APIs without static keys through workload identity federation: the workload presents a short-lived OpenID Connect (OIDC) token issued by its own platform, and the cloud provider exchanges it for temporary credentials. AWS, Google Cloud and Azure all support this exchange with their own names for it. The same mechanism works for a Kubernetes cluster on any cloud and for a GitHub Actions workflow, as long as the cloud can verify the token’s issuer.
Why static keys are the problem
An access key or service account key file is a long-lived bearer credential. Anyone who copies it from a Secret, a CI variable or a log can use it from anywhere until someone revokes it, and rotation means touching every place it was stored. Google’s documentation describes keys as “powerful credentials” that are a security risk to manage. Federation replaces the stored key with a token that expires in minutes or hours and is bound to one workload.
The mechanism: OIDC token exchange
Every federation setup has the same four steps:
- The platform issues a signed token. The token names the workload in its
sub(subject) claim, names the intended recipient in itsaud(audience) claim and expires quickly. - The workload sends the token to the cloud’s token service, such as AWS STS, Google’s Security Token Service or the Microsoft identity platform.
- The cloud verifies the token against the issuer’s public keys, which it finds through the issuer’s OIDC discovery document, and checks the claims against a trust policy you configured.
- The cloud returns temporary credentials scoped to one role or identity, and the workload calls the API with them.
In Kubernetes, the token is a projected service account token. The API server signs it, the kubelet mounts it into the Pod and rotates it once it is older than 80 percent of its lifetime or 24 hours, according to the service account documentation. The sub claim has the form system:serviceaccount:<namespace>:<name>. The API server publishes /.well-known/openid-configuration and the key set at /openid/v1/jwks, so an external system can act as what Kubernetes calls a relying party.
How each cloud names it
| AWS | Google Cloud | Azure | |
|---|---|---|---|
| Feature | IAM OIDC identity provider plus AssumeRoleWithWebIdentity | Workload Identity Federation | Workload identity federation with federated identity credentials |
| Trust object | OIDC provider in IAM, role trust policy | Workload identity pool and provider | Federated credential on an app registration or user-assigned managed identity |
| Claim checks | Condition on :sub and :aud in the trust policy | Attribute mapping and CEL attribute conditions | Exact, case-sensitive match on issuer, subject and audience |
| Result | Temporary credentials for one IAM role | Federated token, used directly or to impersonate a service account | Microsoft Entra access token |
The managed services offer wrappers around this: IAM roles for service accounts and EKS Pod Identity on Amazon EKS, Workload Identity Federation for GKE, and Microsoft Entra Workload ID on AKS.
When the cluster is not on that cloud
The wrappers are tied to their own platform. AWS states that EKS Pod Identity is not available on EKS Anywhere, on AWS Outposts or on Kubernetes clusters you run yourself on EC2, because it depends on an agent that only Amazon EKS provides. A cluster on another cloud or on-premises uses the generic path instead: register the cluster’s issuer URL as an OIDC provider in AWS IAM, a workload identity pool provider in Google Cloud or a federated credential in Azure. Microsoft explicitly lists “workloads running on any Kubernetes cluster” as a supported scenario.
One requirement decides whether this works: the cloud must be able to verify the token. AWS and Azure fetch the discovery document and key set from the issuer URL, so the issuer must be reachable over public HTTPS. If your API server is private, publish the discovery document and keys to a public location and point --service-account-jwks-uri at it. Google Cloud can also accept an uploaded key set instead of fetching it.
CI jobs: GitHub Actions OIDC
GitHub Actions issues OIDC tokens from https://token.actions.githubusercontent.com. A job can request one only when its workflow grants permissions: id-token: write; without it, the GitHub documentation says the token cannot be requested. The sub claim encodes where the job runs, for example:
repo:octo-org/octo-repo:ref:refs/heads/mainfor a branchrepo:octo-org/octo-repo:environment:Productionfor a deployment environmentrepo:octo-org/octo-repo:pull_requestfor a pull request
Write the cloud trust condition against these values. A condition on the environment or a protected branch means a pull request from a fork cannot assume the production role. Avoid wildcards such as repo:octo-org/*, which trust every repository and every branch in the organization. GitLab CI uses the same pattern with id_tokens in the job definition.
The same exchange also works against a Kubernetes API server that trusts GitHub as an identity provider, which removes stored kubeconfigs from CI. The article on deploying to Kubernetes from CI/CD covers that side.
Common errors and their causes
| Symptom | Usual cause |
|---|---|
Not authorized to perform sts:AssumeRoleWithWebIdentity | The sub or aud condition in the role trust policy does not match the token, often a wrong namespace, service account, branch or environment name |
Unable to get ACTIONS_ID_TOKEN_REQUEST_URL in GitHub Actions | The workflow or job lacks id-token: write |
| Audience mismatch | The token was requested for one audience and the provider expects another; set the same value on both sides |
| Issuer or key lookup fails | The issuer URL is private, or its value differs from the iss claim by a character such as a trailing slash |
| Works, then fails after an hour | The application read the token once at startup; the kubelet rotates the file and the application must re-read it |
What this means for secrets
Federation removes the cloud credential, which is often the most valuable secret in a cluster. Database passwords and third-party API keys still need a home, which is the subject of how to manage Kubernetes secrets. Tools such as External Secrets Operator can themselves use workload identity to read from a cloud secret manager, so no bootstrap key is needed either.
Workload identity on Cloudfleet
Every Cloudfleet Kubernetes Engine (CFKE) cluster serves a public OIDC discovery endpoint for its service account tokens, so Pods can federate with AWS, Google Cloud and Azure from any cloud or data center. Accessing cloud APIs securely has the setup for each provider, and the GitHub Actions guide covers OIDC access to the cluster from CI.