Command Palette

Search for a command to run...

Hectal
PHASE 5Advanced ~15 min· topic 5 of 5

Topic 5.5

Workload Identity: Cloud Access Without Stored Keys

In one line

Pods often need cloud APIs: S3 for uploads, SQS for orders, Secrets Manager for credentials. Instead of storing long-lived access keys in Secrets, workload identity maps a Kubernetes ServiceAccount to a cloud role. The pod gets short-lived credentials automatically: on EKS through IRSA or EKS Pod Identity, on GKE through Workload Identity Federation, on AKS through Microsoft Entra Workload ID. Each workload gets only its own permissions, and nothing long-lived can leak.

0/5 · 0%

Think of it like this

A hotel guest's room key versus a master key copy. Instead of giving staff a copy of the master key (stored access keys), the front desk issues each person a card that works only for their rooms and expires at the end of the shift (temporary credentials).

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Workload identity
Giving a pod a cloud identity through its ServiceAccount instead of stored keys.
IRSA
IAM Roles for Service Accounts: EKS's OIDC-based way to map ServiceAccounts to IAM roles.
EKS Pod Identity
A newer EKS mechanism mapping ServiceAccounts to IAM roles through an agent and API associations.
Temporary credentials
Cloud credentials that expire automatically, typically after an hour.
Trust policy
The part of a cloud role saying who is allowed to assume it.
IMDS
The instance metadata service (169.254.169.254) that gives a VM its own credentials.

Step by step

01From stored keys to a role

The uploads service used an IAM user's access key in a Secret. The team replaces it with EKS Pod Identity: a role with S3 permissions for one bucket prefix, associated with the uploads ServiceAccount.

terminal
$ aws eks create-pod-identity-association --cluster-name tiffin-prod --namespace tiffin --service-account uploads --role-arn arn:aws:iam::111122223333:role/tiffin-uploads
aws eks list-pod-identity-associations --cluster-name tiffin-prod --namespace tiffin --query 'associations[].serviceAccount'
── expected output ──
{
"association": {
"clusterName": "tiffin-prod",
"namespace": "tiffin",
"serviceAccount": "uploads",
"roleArn": "arn:aws:iam::111122223333:role/tiffin-uploads",
"associationId": "a-4mkq2xz8p1"
}
}
[
"uploads"
]
From stored keys to a rolediagram
Rendering diagram…

02The pod gets credentials automatically

The Deployment just names the ServiceAccount. No keys anywhere in Kubernetes. The AWS SDK in the pod finds the injected credential endpoint and assumes the role.

k8s/uploads-deployment.yaml (pod spec excerpt)whole fileyaml
spec:
  serviceAccountName: uploads        # mapped to IAM role tiffin-uploads
  containers:
    - name: uploads
      image: ghcr.io/tiffin-team/uploads:1.4.0
      # no AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars at all
terminal
$ kubectl exec deploy/uploads -- aws sts get-caller-identity --query Arn --output text
kubectl exec deploy/uploads -- aws s3 ls s3://tiffin-backups/ 2>&1 | tail -1
── expected output ──
arn:aws:sts::111122223333:assumed-role/tiffin-uploads/eks-tiffin-pro-uploads-6b7c9d5f4-k2x8p
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: User: arn:aws:sts::111122223333:assumed-role/tiffin-uploads/... is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::tiffin-backups"
The pod can use its own bucket prefix and nothing else, as intended.

03Closing the node-role back door

Without care, any pod can call the instance metadata service and get the node's IAM role, which usually has broad permissions. Setting the metadata hop limit to 1 in the node launch template blocks pods (one network hop away) from reaching it.

terminal
$ kubectl run probe --rm -it --image=curlimages/curl:8.10.1 --restart=Never -- curl -s -m2 -X PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60'; echo "exit=$?"
── expected output ──
pod "probe" deleted
exit=28
Timeout: pods can't get a metadata token, so they can't borrow the node's role.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Every pod uses the node's role

Workloads were given permissions by adding policies to the node group's IAM role 'for simplicity'.

terminal
$ kubectl exec deploy/menu-preview -- aws sts get-caller-identity --query Arn --output text
kubectl exec deploy/menu-preview -- aws sqs list-queues --query 'QueueUrls[0]' --output text
── what you'll see ──
arn:aws:sts::111122223333:assumed-role/eks-node-general/i-0a1b2c3d4e5f
https://sqs.ap-south-1.amazonaws.com/111122223333/orders
# a small preview app can read the payment and order queues

Myth vs fact

Myth

Storing access keys in a Kubernetes Secret is fine if the Secret is encrypted.

Fact

Encryption protects them at rest, but the keys still never expire, get copied into many places, and give long-lived access if leaked. Short-lived credentials remove that risk.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Use condition keys in role policies to narrow further, for example restricting an S3 role to one prefix and requiring the request to come from your VPC endpoint (aws:SourceVpce).

Remember this

  1. 1

    The old way: an IAM user's access key and secret in a Kubernetes Secret. They never expire, are often over-privileged, get copied around, and are hard to rotate.

  2. 2

    The new way: the pod's ServiceAccount token (a signed, short-lived JWT) is exchanged with the cloud for temporary credentials for one specific role. SDKs do this automatically.

  3. 3

    EKS: IRSA (an IAM OIDC provider plus a role trust policy naming the ServiceAccount, and an annotation on the ServiceAccount) or the newer EKS Pod Identity (an agent DaemonSet and a 'pod identity association' API, no OIDC setup per cluster).

  4. 4

    GKE: Workload Identity Federation lets a Kubernetes ServiceAccount act as an IAM principal. AKS: Microsoft Entra Workload ID uses federated credentials.

  5. 5

    Least privilege per workload: the uploads service's role can write to one S3 prefix, and the orders worker's role can read one SQS queue. A compromised pod gets only its own small set of permissions, for a short time.

  6. 6

    Block pods from using the node's instance role (IMDS hop limit 1 or blocking 169.254.169.254), or every pod inherits the node's permissions.

Explain it without notes

01

How does a pod get cloud credentials without stored keys?

02

Why block pods from the instance metadata service?

Practice

01

Configure IRSA or Pod Identity for one workload and verify its identity.

02

Find workloads still using static cloud keys.

Trade-offs

  • ↔

    Workload identity removes long-lived keys and gives per-workload permissions, but requires per-cloud setup (OIDC providers, associations, trust policies). Static keys are quick to set up and much riskier to operate.

Done when you can

  • My pods use workload identity, not stored cloud keys.

  • Each workload has its own least-privilege role.

  • Pods can't use the node's IAM role.