BIPI
BIPI

EKS IAM Roles for Service Accounts Misconfigurations

Cloud Security

IRSA is the right pattern for EKS workloads but the trust policy is where it breaks. Missing namespace conditions, broad audiences, and stale role bindings turn a good design into a pivot.

By Arjun Raghavan, Security & Systems Lead, BIPI · February 16, 2025 · 8 min read

#eks#irsa#aws#pentest

IRSA (IAM Roles for Service Accounts) lets a Kubernetes service account assume an AWS IAM role through OIDC. The pattern is correct: short-lived tokens, no long-lived keys, scoped per workload. The implementation routinely is not, and the trust policy on the IAM role is where the slips show up.

How attackers find this

From a foothold inside a pod we read the projected service account token at /var/run/secrets/eks.amazonaws.com/serviceaccount/token, decode it to find the issuer URL and the namespace and service account name claims, and then look at the IAM role trust policy. The trust policy should pin the audience (sts.amazonaws.com) and the subject (system:serviceaccount:<namespace>:<sa-name>). When it does not, any pod in the cluster can assume the role.

  • Trust policy with audience pinned but no sub condition: any service account in the cluster can assume.
  • Trust policy with a wildcard sub like system:serviceaccount:*:default: every namespace's default SA assumes the role.
  • Token volume projection misconfigured so the token is mounted into pods that should not have it.
  • Stale bindings: a service account name that used to belong to a tight workload is now reused by a less-trusted workload.
  • Multiple clusters using the same OIDC provider thumbprint: a token from cluster A passes the trust policy of a role intended for cluster B.

Methodology in practice

We map every role with a trust policy referencing the cluster's OIDC issuer. For each, we check the StringEquals or StringLike conditions on aud and sub. The interesting roles are the ones with a missing sub condition or a permissive pattern. From any pod we can then assume those roles by mounting a service account token whose claims match the loose condition.

Detection

CloudTrail captures every AssumeRoleWithWebIdentity. The events include the OIDC issuer and the subject claim. Detections to write: AssumeRoleWithWebIdentity from a sub that does not match the expected service account for the role, multiple roles assumed by the same sub in a short window, role assumption from a cluster that is not the role's intended cluster (issuer mismatch).

Remediation

  1. Audit every IAM role trust policy that references your EKS OIDC provider; require both aud and sub to be StringEquals exact match.
  2. Scope the sub condition to a specific namespace and service account; never use wildcards.
  3. Rotate the OIDC provider thumbprint when the cluster's CA rotates; stale thumbprints accept old tokens.
  4. Use separate OIDC providers per cluster so a token from a non-prod cluster cannot satisfy a prod role's trust policy.
  5. Consider EKS Pod Identity (the newer mechanism) for new workloads; it removes the OIDC trust dance and uses an EKS-managed credential agent with cleaner scoping.
  6. Set serviceAccountToken volume projection only on pods that need it; default automountServiceAccountToken to false.
  7. Review IRSA role permissions quarterly; the permissions tend to grow as developers add features and rarely get pruned.

IRSA done well is the cleanest identity pattern in EKS. Done poorly it is a quiet escalation primitive that bridges Kubernetes RBAC into AWS IAM. The hardening is mostly in the trust policy, and the trust policy is mostly forgotten after the role is first created.

Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.