An IAM Role for Service Accounts (IRSA) allows your Kubernetes pods running on EKS to assume an AWS IAM role, granting them AWS API permissions without needing to manage static AWS credentials.
Let’s see this in action. Imagine you have a pod that needs to read objects from an S3 bucket.
apiVersion: v1
kind: Pod
metadata:
name: s3-reader-pod
spec:
containers:
- name: app
image: my-s3-reader-app:latest # This app will use the AWS SDK
env:
- name: AWS_ROLE_ARN
value: "arn:aws:iam::123456789012:role/MyEKS-S3ReaderRole" # The ARN of the IAM role
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token" # Path to the OIDC token
serviceAccountName: s3-reader-sa # The Kubernetes Service Account name
This Pod definition is pretty standard, except for those two environment variables. AWS_ROLE_ARN points to the specific IAM role we want this pod to assume. AWS_WEB_IDENTITY_TOKEN_FILE is crucial; it tells the AWS SDK where to find the short-lived OIDC token that Kubernetes provides to the pod’s Service Account. The AWS SDK, when configured to use IRSA, will automatically pick up these environment variables, fetch the token, and use it to exchange for temporary AWS credentials from AWS STS.
Here’s how it all fits together:
- EKS OIDC Provider: Your EKS cluster has an associated IAM OIDC identity provider. This is the trust anchor that allows AWS IAM to verify identities coming from your Kubernetes cluster.
- IAM Role Trust Policy: You create an IAM role. The trust policy of this role is configured to allow the EKS OIDC provider to assume it. Specifically, it trusts
accounts.google.comorcognito-identity.amazonaws.comwith a specificStringEqualscondition that matches your cluster’s OIDC provider URL and the Kubernetes Service Account’s namespace and name. - Kubernetes Service Account: You create a Kubernetes Service Account (e.g.,
s3-reader-sa). You then annotate this Service Account with the ARN of the IAM role you created. This annotation is the link between the Kubernetes Service Account and the AWS IAM role. - Pod Configuration: When you deploy a pod and specify that it should use this Service Account, Kubernetes injects a projected volume containing a JWT (JSON Web Token) signed by the OIDC provider. This token contains claims about the pod’s identity (namespace, Service Account name).
- AWS SDK (or CLI): The AWS SDK running inside your pod (or the AWS CLI configured to use IRSA) reads this JWT from the specified file path. It then makes a call to the AWS Security Token Service (STS)
AssumeRoleWithWebIdentityAPI, presenting the JWT and the ARN of the IAM role. - STS Validation: AWS STS validates the JWT against the trusted OIDC provider and the role’s trust policy. If valid, it issues temporary AWS security credentials (access key ID, secret access key, and session token) to the pod.
- AWS API Calls: Your application code within the pod can now use these temporary credentials (often automatically managed by the SDK) to make authorized AWS API calls.
The problem this solves is the insecure and cumbersome management of static AWS access keys within your Kubernetes cluster. Before IRSA, you’d typically use kops or manually inject aws_access_key_id and aws_secret_access_key into your pods as environment variables or Kubernetes Secrets. This is a security risk and a nightmare to rotate. IRSA centralizes permission management in IAM, leverages short-lived credentials, and integrates natively with Kubernetes RBAC.
The key levers you control are:
- IAM Role Permissions: What AWS actions (e.g.,
s3:GetObject,ec2:DescribeInstances) and resources (e.g.,arn:aws:s3:::my-bucket/*) the pod can access. This is managed entirely within IAM. - Service Account Annotation: The
eks.amazonaws.com/role-arnannotation on the Kubernetes Service Account that maps it to a specific IAM role. - Pod Spec: Which Service Account a pod uses, thereby inheriting the associated IAM role’s permissions.
A detail often overlooked is the precise structure of the StringEquals condition in the IAM role’s trust policy. It must match both the OIDC provider URL and the system:serviceaccounts:namespace:serviceaccount-name string. If either part of this condition is incorrect, the AssumeRoleWithWebIdentity call will fail with a cryptic "Access denied" or "invalid identity token" error, even if the Service Account annotation and OIDC provider are otherwise correctly configured.
The next concept you’ll want to explore is how to effectively manage multiple IRSA roles for different sets of pods with varying permission requirements.