Containers

Session policies for Amazon EKS Pod Identity

Today, we’re announcing the new session policies capability for Amazon Elastic Kubernetes Service (Amazon EKS) Pod Identity. With this new feature, you can dynamically scope down AWS Identity and Access Management (IAM) permissions for your Kubernetes pods without creating additional IAM roles. Session policies give you a flexible alternative to creating separate IAM roles for each permission variation by specifying an inline IAM policy during Pod Identity association creation. This helps you to manage permissions at scale without multiplying your role count. This means that your Kubernetes applications can now operate with precisely the permissions required, following the principle of least privilege, while avoiding reaching IAM role limits in large-scale deployments.

In this post, we demonstrate how to use session policies to dynamically scope down IAM permissions for your Kubernetes pods without creating additional IAM roles, and discuss important considerations when adopting this feature. At re:Invent 2023, Amazon EKS introduced the EKS Pod Identity feature. This feature helps users to configure Kubernetes applications running on Amazon EKS with fine-grained IAM permissions to access AWS resources such as Amazon Simple Storage Service (Amazon S3) buckets and Amazon DynamoDB tables. This feature addressed many of the existing challenges of IAM Roles for Service Accounts (IRSA) by removing the need to set up OpenID Connect (OIDC) providers for EKS clusters, streamlining IAM trust policies, and streamlining the experience through Amazon EKS APIs. Furthermore, it introduced support for IAM role session tags, so IAM administrators can author a single permissions policy that can work across roles by allowing access to AWS resources based on matching tags.

Through nearly continuous user feedback, we learned that customers often face challenges when they need different permission levels for pods running the same application. Common scenarios include the following:

  • Platform Engineering teams managing multi-tenant EKS clusters where different customer workloads need varying levels of access to the same AWS services. For example, a software as a service (SaaS) application where each pod operates against the tenant specific resources and data and must assume only that tenant’s specific roles.
  • Teams running multiple environments (dev, test, staging) in the same cluster where pods need different permission scopes based on their environment without creating separate IAM roles for each.
  • Data processing workloads where pods need access to different subsets of S3 buckets or DynamoDB tables based on their specific function, but creating individual IAM roles for each variation would exceed the 5,000 IAM roles per account quota.

Previously, you had to choose between creating separate IAM roles (hitting quota limits), granting overly broad permissions (security risk), or using session tags (not supported by all AWS services) to handle these scenarios. To address these challenges and support fine-grained permission control, we’re launching session policies for EKS Pod Identity. You can use session policies to apply inline IAM policies when EKS Pod Identity assumes an IAM role on behalf of your pods. These policies create an intersection between the permissions granted by the IAM role and the session policy. This effectively restricts permissions to only what’s explicitly allowed in both policies. This feature works for both same-account scenarios and cross-account access patterns using IAM role chaining. For in-depth policy evaluation logic, refer to IAM Policy evaluation for single account and cross-account scenarios.

What is changing in EKS Pod Identity APIs

The following APIs are updated to introduce new request and response elements to pass session policies:

CreatePodIdentityAssociation: API call to create an EKS Pod Identity association between a service account in an EKS cluster and an IAM role with EKS Pod Identity.

Request parameters:

  • clusterName: The name of the cluster in which to create the association.
  • namespace: The name of the Kubernetes namespace inside the cluster in which to create the association.
  • serviceAccount: The name of the Kubernetes service account inside the cluster with which to associate the IAM credentials.
  • roleArn: The Amazon Resource Name (ARN) of the IAM role to associate with the service account.
  • targetRoleArn: The ARN of a target IAM role in another AWS account. When specified, EKS Pod Identity performs IAM role chaining: it first assumes the roleArn, then uses those credentials to assume the targetRoleArn. When you specify a session policy, EKS Pod Identity applies it when assuming the target role, not the source role.
  • policy (new): An IAM policy in JSON format that you want to use as an inline session policy. This parameter is optional. The resulting session’s permissions are the intersection of the role’s identity-based policy and the session policy. The plaintext policy can’t exceed 2,048 characters.
  • disableSessionTags: Defaults to false (session tags enabled) when not specified. Session tags cannot be used in combination with session policies due to AWS Security Token Service (AWS STS) policy size quota.

UpdatePodIdentityAssociation: API to update an existing pod identity association. Use this to update the IAM role, target IAM role, session policy, or disableSessionTags attributes of the association.

Request parameters:

  • clusterName: The name of the cluster where the association exists
  • associationId: The ID of the association to be updated
  • roleArn: The new IAM role to associate with the service account (optional)
  • targetRoleArn: The new target IAM role to associate with the service account
  • policy (new): The new session policy to associate with the service account (optional)
  • disableSessionTags: Boolean flag to enable or disable session tags (optional, but must be true when the policy is specified)

How to get started

We demonstrate how to use session policies to restrict a Kubernetes pod’s permissions to only list S3 buckets, even though the underlying IAM role has broader S3 permissions including the ability to create buckets.

Prerequisites

The following prerequisites are necessary to complete this solution:

Setup

export AWS_REGION=us-west-2  # Replace with your AWS Region
export CLUSTER_NAME=session-policy-demo  # Replace with your EKS cluster name
export NAMESPACE=demo-ns
export AWS_ACCOUNT=$(aws sts get-caller-identity --query Account --output text) # Replace with your AWS Account number
export SERVICE_ACCOUNT=s3-demo-sa

Step 1: Create an EKS cluster with Pod Identity add-on

Let’s start by creating an Amazon EKS Cluster using eksctl with the new eks-pod-identity-agent add-on.

cat << EOF > cluster.yaml 
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: ${CLUSTER_NAME}
  region: ${AWS_REGION}
  version: "1.35"

addons:
  - name: vpc-cni
  - name: coredns
  - name: kube-proxy
  - name: eks-pod-identity-agent 
    
managedNodeGroups:
  - name: ${CLUSTER_NAME}-mng
    privateNetworking: true
    minSize: 1
    desiredCapacity: 1
    maxSize: 1
EOF

eksctl create cluster -f cluster.yaml

It takes approximately 15 minutes to get the cluster infrastructure created, you can verify the status using the following command or Amazon EKS console.

aws eks describe-cluster --name ${CLUSTER_NAME} --region ${AWS_REGION} --query 'cluster.status'

The output of this command should be ACTIVE.

After the cluster creation is completed, confirm that the eks-pod-identity-agent add-on is running in the cluster.

eksctl get addon --cluster ${CLUSTER_NAME} --region ${AWS_REGION} --name eks-pod-identity-agent -o json 

[
    {
        "Name": "eks-pod-identity-agent",
        "Version": "v1.3.10-eksbuild.2",
        "NewerVersion": "",
        "IAMRole": "",
        "Status": "ACTIVE",
        "ConfigurationValues": "",
        "Issues": null
    }

Step 2: Create an IAM role with broad S3 permissions

Create an IAM role with permissions to perform multiple S3 operations, including listing and creating buckets.

cat << EOF > role_trust_policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "pods.eks.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession"
            ]
        }
    ]
}
EOF

cat << EOF > role_permission_policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListAllMyBuckets",
                "s3:CreateBucket",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "*"
        }
    ]
}
EOF

aws iam create-role --role-name session-policy-demo-role \
    --assume-role-policy-document file://role_trust_policy.json
aws iam put-role-policy --role-name session-policy-demo-role \
    --policy-name S3BroadAccess \
    --policy-document file://role_permission_policy.json

Step 3: Create a Pod Identity association without a session policy

Create a Kubernetes namespace and service account, then associate them with the IAM role.


kubectl create namespace ${NAMESPACE}
kubectl create serviceaccount ${SERVICE_ACCOUNT} -n ${NAMESPACE}

aws eks create-pod-identity-association \
    --cluster-name ${CLUSTER_NAME} \
    --namespace ${NAMESPACE} \
    --service-account ${SERVICE_ACCOUNT} \
    --role-arn arn:aws:iam::${AWS_ACCOUNT}:role/session-policy-demo-role \
    --region ${AWS_REGION}

Step 4: Test the pod with full IAM role permissions

Deploy a test pod and verify it can perform multiple S3 operations.

kubectl run s3-test --image=amazon/aws-cli:latest \
    --namespace=${NAMESPACE} --rm -it --restart=Never \
    --overrides='{"spec":{"serviceAccountName":"'${SERVICE_ACCOUNT}'"}}' \
    -- s3 ls

kubectl run s3-test --image=amazon/aws-cli:latest \
    --namespace=${NAMESPACE} --rm -it --restart=Never \
    --overrides='{"spec":{"serviceAccountName":"'${SERVICE_ACCOUNT}'"}}' \
    -- s3 mb s3://session-policy-test-bucket-$(date +%s)

Both commands should succeed, demonstrating that the pod has full permissions granted by the IAM role.

Step 5: Add a session policy to restrict permissions

Now, update the Pod Identity association to include a session policy that only allows listing S3 buckets.

ASSOCIATION_ID=$(aws eks list-pod-identity-associations \
    --cluster-name ${CLUSTER_NAME} \
    --namespace ${NAMESPACE} \
    --service-account ${SERVICE_ACCOUNT} \
    --region ${AWS_REGION} \
    --query 'associations[0].associationId' \
    --output text)

aws eks update-pod-identity-association \
    --cluster-name ${CLUSTER_NAME} \
    --association-id ${ASSOCIATION_ID} \
    --policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:ListAllMyBuckets","Resource":"*"}]}' \
    --disable-session-tags \
    --region ${AWS_REGION}

Important: The --disable-session-tags flag is required when using session policies. Session tags and session policies cannot be used together due to packed policy size limitations enforced by AWS STS. If you attempt to create or update a pod identity association with both a policy and session tags enabled (or omit the --disable-session-tags flag), you will receive a PackedPolicyTooLarge validation error during the API call.

Step 6: Verify the restricted permissions

Due to the eventual consistency nature of the EKS Pod Identity API, it can take a few seconds to propagate the update. Run the following commands to test the scenario again.

Security Note: During the propagation window (up to 10 seconds), pods continue to receive credentials with previous permissions. Plan permission changes accordingly and monitor AWS CloudTrail for any unauthorized access attempts during the transition window.

kubectl run s3-test --image=amazon/aws-cli:latest \
    --namespace=${NAMESPACE} --rm -it --restart=Never \
    --overrides='{"spec":{"serviceAccountName":"'${SERVICE_ACCOUNT}'"}}' \
    -- s3 ls

kubectl run s3-test --image=amazon/aws-cli:latest \
    --namespace=${NAMESPACE} --rm -it --restart=Never \
    --overrides='{"spec":{"serviceAccountName":"'${SERVICE_ACCOUNT}'"}}' \
    -- s3 mb s3://session-policy-test-bucket-2-$(date +%s)

The first command succeeds because s3:ListAllMyBuckets is allowed by both the IAM role and the session policy. The second command fails with an “Access Denied” error because s3:CreateBucket is not included in the session policy, even though the IAM role permits it.

Step 7: Expand the session policy

You can update the session policy at any time to grant additional permissions (up to what the IAM role allows).

aws eks update-pod-identity-association \
    --cluster-name ${CLUSTER_NAME} \
    --association-id ${ASSOCIATION_ID} \
    --policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:ListAllMyBuckets","s3:CreateBucket"],"Resource":"*"}]}' \
    --region ${AWS_REGION}

After waiting for the credential cache to expire, the pod will now be able to both list and create S3 buckets. AWS CloudTrail records all events, so you can audit and monitor all IAM role activity, including the session policies, for security and compliance purposes.

Understanding session policy validation

EKS Pod Identity validates session policies when you create or update a pod identity association, providing immediate feedback if there are any issues. The validation process includes:

  1. JSON format validation: EKS Pod Identity validates that the policy is valid JSON
  2. Character validation: EKS Pod Identity verifies that your policy contains only valid characters
  3. Size validation: EKS Pod Identity confirms that the policy doesn’t exceed 2,048 characters
  4. IAM policy schema validation: EKS Pod Identity validates against IAM policy structure requirements by performing a dry-run call to AWS STS
  5. Packed policy size validation: EKS Pod Identity makes sure that the session policy doesn’t exceed STS limits after compression

Important considerations

This section outlines key constraints and requirements before adopting session policies with EKS Pod Identity. It covers their interaction with session tags, permission evaluation rules, and how policies are applied in same and cross-account scenarios.

Session tags and session policies

Session tags can’t be used with session policies. When you specify a session policy, you must set --disable-session-tags to true. This is a requirement, not an optional optimization.

Permission intersection

Session policies can only restrict permissions, not expand them. The effective permissions are always the intersection of:

  1. The IAM role’s identity-based policies
  2. The session policy (if provided)

This ensures that session policies maintain security boundaries and cannot be used to escalate privileges beyond what the IAM role allows.

Session policy application

Session policies are applied differently depending on whether you’re using a target role. A target role enables cross-account access scenarios where your pods must assume an IAM role in a different AWS account. When you specify a target role, EKS Pod Identity performs the IAM role chaining: it first assumes the source role (roleArn) in your account, then uses those credentials to assume the target role (targetRoleArn) in another account.

When targetRoleArn is specified (cross-account scenarios):

  • The session policy is applied when assuming the target role, not the initial source role
  • The source role (roleArn) needs permission to assume the target role using sts:AssumeRole and sts:TagSession actions
  • The session policy restricts the permissions of the target role through IAM role chaining

When targetRoleArn is not specified (same-account scenarios):

  • EKS Pod Identity applies the session policy directly to the roleArn when it assumes the role
  • The effective permissions are the intersection of the role’s identity-based policies and the session policy

Other considerations

Following are additional considerations on service and AWS Region availability, Kubernetes version, and infrastructure as code (IaC) requirements.

  • Kubernetes version support: The EKS Pod Identity session policy feature is supported on Kubernetes versions supported in Amazon EKS.
  • Region availability: Session policies are available in AWS Commercial, Mainland China, and AWS GovCloud (US) Regions where Amazon EKS is supported.
  • Role association limits: You can associate only one IAM role to a Kubernetes service account using EKS Pod Identity. However, you can update the role, target role, and session policy later.
  • Tooling support: You can use AWS CloudFormation, eksctl, AWS CLI, and Amazon EKS API to create pod identity associations with session policies.

Cleaning up

Important: Complete these cleanup steps to avoid ongoing charges for the resources created in this walkthrough:

aws eks delete-pod-identity-association \
    --cluster-name ${CLUSTER_NAME} \
    --association-id ${ASSOCIATION_ID} \
    --region ${AWS_REGION}

kubectl delete serviceaccount ${SERVICE_ACCOUNT} -n ${NAMESPACE}
kubectl delete namespace ${NAMESPACE}

eksctl delete cluster -f cluster.yaml

aws iam delete-role-policy --role-name session-policy-demo-role --policy-name S3BroadAccess
aws iam delete-role --role-name session-policy-demo-role

Conclusion

In this post, we demonstrated the new session policy capability of Amazon EKS Pod Identity, which enables fine-grained permission control for Kubernetes workloads without the overhead of managing thousands of IAM roles. With dynamic permission scoping at the Pod Identity level, this feature helps you build more secure, scalable, and maintainable applications on Amazon EKS while following least privilege. We encourage you to start using this feature and share your feedback at AWS Containers Roadmap.

Additional resources


About the authors

Aditya Potdar is a Software Engineer on the Amazon EKS team at Amazon Web Services. With experience spanning multiple leading technology companies, he specializes in architecting and delivering container-based infrastructure that powers large-scale production systems and machine learning workloads.

Ashok Srirama is a Principal Solutions Architect at Amazon Web Services, based in Washington Crossing, PA. He specializes in serverless applications, containers, and architecting distributed systems. When he’s not spending time with his family, he enjoys watching cricket, and driving his bimmer.

George John is a Senior Product Manager for Amazon Elastic Kubernetes Service (EKS) at AWS, where he drives product strategy and innovation for one of the industry’s leading managed Kubernetes platforms. In his role, George works closely with customers, partners, and the broader cloud-native community to shape the future of container orchestration on AWS. When he is not building products, he loves to explore the Pacific Northwest with his family.