Workload identity: SPIFFE, IRSA, GCP WIF, Azure WI
Replace long-lived secrets with short-lived, attested workload credentials across Kubernetes and the big three clouds.
Cross-ref (Microsoft stack): In Entra Agent ID, workload identity federates into the platform via Federated Identity Credentials (FIC) on agent blueprints. See Lesson 10.3 (FIC + managed identities on blueprints) and Lesson 10.9 (federating SPIFFE / AWS STS / GCP WIF / GitHub Actions OIDC to Entra Agent ID). Every primitive here has a direct Microsoft equivalent — you don't rebuild, you federate.
Why this matters
Where do your agent's credentials come from? If the answer is "an env var with a static API key", you've already lost — any sub-agent, log scraper, or supply-chain compromise can walk away with them. Modern systems give every workload a short-lived, cryptographically-bound identity issued by the infrastructure it runs on. Pair that with the OAuth flows from lesson 2.1 and you've eliminated an entire class of credential-theft attacks.
Learning objectives
- Use SPIFFE/SPIRE for cloud-neutral workload identity.
- Use AWS IRSA / Pod Identity to give K8s pods AWS IAM roles.
- Use GCP Workload Identity Federation.
- Use Azure Workload Identity.
- Exchange a workload SVID/JWT for an OAuth access token at your IdP.
1. The problem in one sentence
Every running agent process needs a provable identity, issued automatically by the infrastructure, rotating frequently, with no human in the loop, that downstream services can verify.
Static secrets violate every property: they're stored at deploy time (often forever), they're copy-pastable, they don't rotate, and verification means "the bearer knows the secret" rather than "the bearer is who they claim".
2. SPIFFE / SPIRE — the open standard
SPIFFE (Secure Production Identity Framework For Everyone) is a CNCF standard. SPIRE is its reference implementation. The core ideas:
- SVID (SPIFFE Verifiable Identity Document): a short-lived X.509 cert or JWT identifying a workload.
- SPIFFE ID format:
spiffe://<trust_domain>/<path>e.g.spiffe://acme.com/agents/helpdesk-v1. - Workload API: a Unix-domain socket that workloads call to fetch their SVID. SPIRE Agent on the node attests the workload (via process metadata, K8s service account, host certs) and issues an SVID it can use.
Why SPIFFE rocks:
- Cloud-neutral (works on bare metal, K8s, multi-cloud, VMs).
- Short-lived (default 1 h X.509, 5 min JWT).
- Strong attestation (a process can't claim to be a different SPIFFE ID).
- mTLS-friendly: the SVID is a TLS cert.
A minimal SPIRE on K8s
helm repo add spiffe https://spiffe.github.io/helm-charts-hardened/
helm install spire spiffe/spire \
-n spire-system --create-namespace \
--set global.spire.trustDomain=acme.comWorkload annotation makes a pod's identity:
metadata:
annotations:
spiffe.io/spire-managed-identity: "true"
spec:
serviceAccountName: helpdesk-agentFetching an SVID from Python
# uv add spiffe
from spiffe import WorkloadApiClient
client = WorkloadApiClient()
x509_ctx = client.fetch_x509_svid_context()
print(x509_ctx.default_svid.spiffe_id)
print(x509_ctx.default_svid.cert_chain[0].subject)For JWT-SVIDs (use with OAuth):
jwt = client.fetch_jwt_svid(audiences=["https://idp.acme.com/"]).tokenThis is the credential you'll exchange for an OAuth access token.
3. Cloud-native workload identity
If you're all-in on one cloud, the cloud has a native equivalent.
AWS — IRSA + EKS Pod Identity
Old way (IRSA): K8s pod's service account is annotated with an IAM role ARN. The pod's SDK fetches WebIdentityToken (a signed JWT from the cluster's OIDC issuer) and exchanges it via STS for short-lived AWS creds.
apiVersion: v1
kind: ServiceAccount
metadata:
name: helpdesk-agent
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/HelpdeskAgentimport boto3
s3 = boto3.client("s3") # SDK auto-uses IRSA credsNew way (Pod Identity, 2023+): even simpler — bind a role to a service account in EKS console / CLI; no annotations, no env vars, no OIDC trust policy gymnastics. Strongly preferred for new clusters.
GCP — Workload Identity Federation (WIF)
- Configure a Workload Identity Pool + Provider that trusts your K8s cluster's OIDC issuer (or any OIDC).
- Annotate the K8s SA with the GCP SA email it should impersonate.
- The pod fetches a Google access token via the metadata server.
apiVersion: v1
kind: ServiceAccount
metadata:
name: helpdesk-agent
annotations:
iam.gke.io/gcp-service-account: helpdesk-agent@acme.iam.gserviceaccount.comWIF also works outside GKE — any OIDC-issuing system (GitHub Actions, AWS, even SPIFFE) can be a provider. This is the canonical way to let GitHub Actions deploy to GCP without static keys.
Azure — Workload Identity
Same pattern: Azure AD Federated Identity Credential trusts the K8s OIDC issuer; pod fetches an Entra ID token via the AKS workload identity webhook.
apiVersion: v1
kind: ServiceAccount
metadata:
name: helpdesk-agent
annotations:
azure.workload.identity/client-id: 00000000-0000-0000-0000-0000000000004. From workload identity to OAuth access token
Workload identity gets you a cloud credential or SPIFFE SVID. But your IdP (Keycloak / Auth0 / Okta) issues OAuth tokens. Bridge the two with JWT Bearer Grant (RFC 7523) or Token Exchange (RFC 8693).
# Agent has a JWT-SVID from SPIRE; exchange it at the IdP for an access token
import requests
jwt_svid = WorkloadApiClient().fetch_jwt_svid(audiences=[ISSUER]).token
token = requests.post(
f"{ISSUER}/protocol/openid-connect/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": jwt_svid,
"scope": "tickets:read tickets:write",
},
verify=False,
).json()Configure the IdP to trust SVIDs from your SPIRE trust domain (add SPIRE's JWKS as a trusted issuer; map sub=spiffe://acme.com/agents/helpdesk-v1 to client agent.helpdesk-v1).
Result: your agent never sees a static client secret. It boots, fetches an SVID via the workload API, exchanges it for an OAuth token, and starts working. Restart = new SVID + new token. Eviction = creds gone in seconds.
5. Service-to-service with mTLS (SVIDs as TLS certs)
Inside your network, services authenticate each other directly via mutual TLS using their SVID certs:
import grpc
from spiffe import WorkloadApiClient
# Auto-rotating mTLS credentials
client = WorkloadApiClient()
channel = grpc.secure_channel(
"tickets-api:50051",
grpc.ssl_channel_credentials(
root_certificates=client.fetch_x509_bundles().bundles[0].x509_authorities[0].public_bytes(),
private_key=client.fetch_x509_svid_context().default_svid.private_key_pem,
certificate_chain=client.fetch_x509_svid_context().default_svid.cert_chain_pem,
),
)No tokens to leak. Identity = the cert. Servers verify the client SPIFFE ID and apply policy.
Pairs naturally with service meshes (Istio, Linkerd, Cilium ServiceMesh, Consul Connect) — they SPIFFE-issue automatically and enforce identity-based policy without app code changes.
6. Choosing your workload identity stack
| Situation | Pick |
|---|---|
| Single cloud, K8s only | Native (IRSA / WIF / Azure WI) |
| Multi-cloud or bare metal | SPIFFE/SPIRE |
| Service mesh present | Istio / Linkerd (SPIFFE under the hood) |
| Serverless (Lambda, Cloud Run) | Native runtime identity + WIF for cross-cloud |
| Developer laptop | OAuth device flow against IdP (gh auth login-style) |
Avoid: long-lived API keys in a .env. Even in dev, prefer short-lived creds via SSO + STS / IDP.
7. Federating into your IdP
For each agent runtime you operate:
- Workload gets cloud-native or SPIFFE identity.
- IdP is configured to trust that identity provider.
- Agent exchanges native identity → OAuth access token.
- Agent uses OAuth token to call tools / APIs.
Keycloak supports this via Identity Brokering + JWT Bearer Grant. Auth0 supports it via Custom Token Exchange. Okta supports via OAuth for Server-to-Server.
Wiring this up once eliminates 90% of agent credential-handling code.
8. Rotating, revoking, and emergencies
- Rotation: workload identity tokens default to 5-60 min. No code change needed; SDK refreshes.
- Revocation: revoke at the source (delete K8s SA / disable cloud SA). Workload's next refresh fails; running tokens expire within minutes.
- Emergency: nuke the trust relationship (delete the WIF provider, the IRSA OIDC trust, or the SPIRE trust domain bundle). All federated tokens stop minting immediately.
This recoverability is exactly what static API keys lack. Practice the drill quarterly.
9. Hands-on lab (3 h)
- Install SPIRE on
kind(spiffe/spireHelm chart). Verify the workload API socket inside a pod. - Deploy a small Python agent that fetches its X.509-SVID and prints the SPIFFE ID.
- Configure Keycloak to trust SPIRE as a JWT issuer; map
spiffe://acme.com/agents/helpdesk-v1to the Keycloak clientagent.helpdesk-v1. - Have the agent fetch a JWT-SVID and exchange it for an OAuth access token (RFC 7523).
- Call your earlier
/meresource server; confirmsubis now the workload + tied to the agent client. - Stretch: switch to mTLS between agent and
tickets-apiusing SVIDs as the TLS material.
10. Common pitfalls
- Static AWS keys baked into Dockerfiles — still common; instantly findable in image layers.
- Trusting any OIDC issuer in your WIF config — restrict by
aud+subto specific service accounts. - SPIRE Server unreachable → workloads can't refresh → silent outages. Add SLOs.
- Long-lived JWT-SVIDs (1h+) — defeat the purpose; keep them under 15 min for sensitive workloads.
- Allowing per-namespace IAM mapping to a high-privilege role — over-permissive.
- Forgetting that mTLS authenticates identity, not authorization — still need policy.
11. Self-check
- Define SVID.
- IRSA vs EKS Pod Identity.
- How GCP WIF + GitHub Actions removes the need for service-account JSON keys.
- RFC 7523 in one sentence.
- Why mTLS between services beats bearer tokens inside the cluster.
12. References
- SPIFFE/SPIRE docs (
spiffe.io). - AWS docs: "IAM Roles for Service Accounts" + "EKS Pod Identity".
- GCP "Workload Identity Federation" docs.
- Azure "Workload Identity for AKS".
- RFC 7523 (JWT Bearer Grant).
- RFC 9068 (JWT profile for OAuth access tokens).
- "Zero Trust" — Evan Gilman + Doug Barth (O'Reilly).
- Cloud Native Computing Foundation SPIFFE whitepaper.
Sign in to save your progress and earn badges.