Guide

Applications used to carry long-lived cloud keys. Those keys never expire, get copied into logs, and outlive the engineer who made them. Workload identity replaces them. The running workload proves who it is, and the cloud hands it temporary credentials.

Concept

Assume-role means exchanging one set of credentials for another, usually short-lived. The caller proves it is allowed to become a role. The cloud returns an access key, a secret and a session token that expire in minutes to hours. Nothing long-lived has to sit in the application.

The big idea

Think of a visitor badge printed at the front desk. It opens the doors you need, and it stops working at the end of the day. You never carry a permanent master key.

The badge analogy breaks in one place. A visitor badge can be handed to someone else, and a stolen temporary credential still works until it expires. Short expiry limits the damage. It does not remove it.

Walking through the chain

Here is what happens when a pod on EKS calls S3:

  1. The pod mounts a projected token that identifies its Kubernetes service account.
  2. The AWS SDK sends that token to STS with AssumeRoleWithWebIdentity.
  3. STS checks the trust policy on the role. It accepts only the expected subject.
  4. STS returns temporary credentials. The pod uses them until they expire, then gets new ones.

You configure three things:

  • An OIDC provider for the cluster, trusted by IAM. EKS creates one for you.
  • A trust policy on the role that allows the call only when sub matches system:serviceaccount:namespace:name.
  • An annotation on the service account that names the role. GCP's Workload Identity binds a Kubernetes service account to a Google service account the same way.

Rotating access means changing the role policy or the binding. Nothing in the cluster needs a new file.

The same idea for CI

Build pipelines are high-value identities. They push images and apply Terraform. Instead of storing a cloud key in CI secrets, GitHub Actions presents a short-lived token. The cloud checks its subject, for example repo:acme/payments:ref:refs/heads/main, and grants a role scoped to that repo and branch.

Split the roles. A plan role can read. An apply role can change things, and it should need manual approval for production.

Common mistakes

  • Leaving the metadata service open. Anything that reaches 169.254.169.254 gets the instance's credentials. Use IMDSv2, and keep the instance role minimal.
  • Using Principal: "*" in a trust policy. Anyone who guesses the role name can try to assume it. Pin the trust to one account, repo or service account.
  • Giving the CI role admin in every account. A leaked CI role then becomes a company-wide bug.

Warning

In the 2019 Capital One breach, a misconfigured web firewall could reach the metadata service. The role behind it could list and read sensitive S3 data. The buckets were not public. The role was too powerful. Lock down metadata access and keep roles small.

Check yourself

  1. Why is a short-lived credential safer than a static key? It expires on its own, so a leaked copy stops working after a limited time.
  2. A trust policy lets any workload in the org assume a role. What is wrong? An unexpected workload can assume it. Pin the subject to one repo, branch or service account.

Key takeaways

  • Workloads should prove identity and receive short-lived credentials, not keys.
  • The trust policy is the lock. Pin it to a specific subject.
  • CI uses OIDC federation, with separate plan and apply roles.
  • The metadata service is a credential source. Protect it with IMDSv2 and small roles.

Further learning

Next, see how people log in and how org-level policies cap every admin in org guardrails and human access.