Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

AWS EKS Consulting

IAM Roles for Service Accounts on EKS: Least Privilege per Workload

IRSA gives each Kubernetes service account its own AWS IAM role through the cluster's OIDC provider, so pods get only the permissions they need instead of inheriting the node's role. We design, codify and test those roles per workload, and advise when EKS Pod Identity is a better fit.

Key takeaways

  • Without IRSA or Pod Identity, every pod on a node can use the node's IAM role, which is almost always too broad.
  • One role per workload, scoped to specific resources and actions, limits damage from a compromised pod.
  • Trust policies must pin the exact namespace and service account, or any pod in the cluster may assume the role.
  • EKS Pod Identity is a simpler alternative for many cases, and we choose per cluster.
  • Roles are defined in Terraform and reviewed like any other code.

The problem IRSA solves

By default, a pod on an EKS node can reach the instance metadata service and obtain the credentials of the node’s IAM role. That role usually exists to let the node pull images and join the cluster, and over time it collects extras: S3 read access added for one team, a queue permission for another. Every pod on that node inherits all of it.

That is the wrong shape. If a single service has a remote code execution bug, the attacker gets whatever the node can do. This page describes how we fix that as part of AWS EKS consulting, and it is a core control in our wider EKS security work.

What good looks like

  • No application pod relies on the node role for AWS access.
  • Each workload that calls AWS has its own role, named and tagged for its owner.
  • Permissions name specific resources, not wildcards, and use conditions where helpful.
  • Trust policies pin one namespace and service account.
  • Roles are code, reviewed in pull requests, and checked regularly for unused permissions.

How IRSA works

The cluster has an OIDC identity provider registered in IAM. When a pod uses a service account annotated with a role ARN, the EKS pod identity webhook injects a projected service account token and environment variables. The AWS SDK exchanges that token with STS using AssumeRoleWithWebIdentity and receives short-lived credentials for the role.

The security of the whole scheme rests on the trust policy of the role. A careful one looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:payments:invoice-worker",
          "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}

The account ID and provider ID above are illustrative. The sub condition is the important line: if it is missing or uses a wildcard, any service account in the cluster may assume the role. We audit for this in every engagement.

The service account then links to the role:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: invoice-worker
  namespace: payments
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/payments-invoice-worker

IRSA or EKS Pod Identity

EKS Pod Identity is the newer mechanism. An agent add-on runs on nodes, and you associate a role with a service account through the EKS API instead of editing OIDC trust policies. Roles use a fixed trusted service principal, which makes reuse across clusters simpler and avoids the per-cluster OIDC provider.

FactorIRSAEKS Pod Identity
Works outside EKSYes, with OIDCEKS only
Trust policyCluster specific OIDCShared service principal
SetupOIDC provider and annotationsAdd-on and associations
Fargate supportYesCheck current support for your case

We often use Pod Identity for new EKS-only clusters and keep IRSA where clusters already use it or where portability matters. Mixing both in one cluster is possible and sometimes the pragmatic path during migration. Confirm current feature support in AWS documentation before committing.

Deriving least privilege

Writing the minimum policy is where effort goes. Our method:

  1. Read the application code and configuration to list intended AWS calls.
  2. Run it in a test cluster with a broader role and inspect CloudTrail for the actions actually used.
  3. Use IAM Access Analyzer policy generation as a starting point, never as the final answer.
  4. Restrict by resource ARN, add conditions such as required tags or source VPC endpoints, and remove wildcards.
  5. Test that unrelated actions are denied.

Each role is defined in Terraform alongside the workload’s other infrastructure. See Terraform modules and state for how these layers are organised.

Locking down the node role

IRSA only helps if pods cannot fall back to the node role. We set the instance metadata options to require IMDSv2 with a hop limit of 1 on worker nodes, which stops regular pods from reaching the metadata service. Pods that need host networking are reviewed individually. Pairing this with network policies and security groups closes more paths.

A worked example: a queue consumer

Take a worker that reads messages from one SQS queue and writes results to one S3 prefix. A first draft often arrives as a broad managed policy for each service. The least-privilege version names the single queue ARN with receive and delete actions, and the single bucket prefix with put and get actions. If the queue is encrypted with a customer managed key, a narrow decrypt permission on that key is added too.

Then we prove it. We run the worker in a test cluster, confirm it works, and attempt a call it should not be able to make, such as listing every bucket. That call must fail with an access denied error. Keeping that failing test in the repository means a future permission change that quietly widens access is caught in review.

Reviewing roles over time

Roles that were tight on day one tend to widen. We schedule a periodic review of last-accessed data and Access Analyzer findings for each workload role, remove actions that have not been used, and check that every role still has a named owner. Decommissioned services should have their roles deleted along with their service accounts, so nothing is left assumable by a name someone might reuse.

Common failure modes

  • Wildcard trust policies, covered above.
  • Shared roles across many services, which defeats the isolation. One role per service account is the rule.
  • Add-on roles forgotten, leaving the CSI driver or load balancer controller on the node role.
  • Role drift when permissions are widened during an incident and never reversed. Terraform plans catch this.
  • Token audience mismatches when using custom SDK setups or older SDK versions that do not support web identity credentials.

Delivery and handover

You receive Terraform for the OIDC provider or Pod Identity setup, one role per workload, tests that prove denied actions fail, and a short guide for adding a role for a new service. We also give your developers a template so new workloads follow the pattern by default. Related controls such as KMS secrets encryption use these same roles to grant key access.

How to tell it is working

Look at CloudTrail: calls from your pods should appear under per-workload role sessions, not under the node instance role. IAM Access Analyzer should show no unused-permission findings that you cannot explain. If you would like us to review your current roles, contact us.

References

How we deliver it

  1. 01

    Inventory AWS access

    We find which workloads call AWS APIs today and which permissions they really use, from code and CloudTrail.

  2. 02

    Block node role inheritance

    Pod access to instance metadata is restricted so workloads cannot quietly use the node role.

  3. 03

    Design role boundaries

    One role per service account is drafted with resource-level permissions and conditions.

  4. 04

    Create trust and policies as code

    OIDC provider, trust policies and permission policies are written in Terraform modules.

  5. 05

    Annotate and roll out

    Service accounts are linked to roles and workloads are restarted and verified in stages.

  6. 06

    Verify and monitor

    We test denied actions, review access analysis findings and set an ongoing review routine.

Tools and technologies

AWS IAMIRSAEKS Pod IdentityOpenID ConnectAWS CloudTrailIAM Access AnalyzerTerraformAWS STSkubectlKyverno

Frequently asked questions

What is IRSA in EKS?
IRSA, IAM roles for service accounts, lets a Kubernetes service account assume an AWS IAM role using a web identity token issued by the cluster's OIDC provider. Pods using that service account receive temporary credentials for just that role, so permissions are granted per workload rather than per node.
IRSA or EKS Pod Identity?
Both give pods their own role. IRSA needs an OIDC provider per cluster and role trust policies that reference it. Pod Identity uses an agent add-on and an association API, which simplifies trust policies and reuse across clusters. IRSA works widely, including outside EKS, so the choice depends on your setup.
Why not just attach permissions to the node role?
Because every pod on the node can then use them. A compromised web pod could read your S3 buckets or secrets meant for another service. Per-workload roles contain the blast radius and make audit logs show which application did what.
How do you work out the minimum permissions?
We start from what the code calls, then validate against CloudTrail and IAM Access Analyzer policy generation, and tighten with resource ARNs and conditions. Then we test in a non-production cluster. Least privilege is iterative, and we leave a process for keeping it that way.
Do system add-ons need IRSA too?
Yes. Components such as the EBS CSI driver, the AWS Load Balancer Controller and Karpenter all call AWS APIs and should each have their own scoped role. We set these up alongside the add-ons.

Talk to an engineer about eks irsa

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.