Skip to main content

More Info:

Use IAM roles for service accounts (IRSA) to associate dedicated IAM roles with Kubernetes service accounts, giving pods least-privilege AWS access instead of relying on the shared node role.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS EKS

Triage and Remediation

Remediation

Manual Steps

  1. Identify pods that use AWS APIs and how they get credentials
    • On any machine with AWS CLI access:
    • On any machine with kubectl access:
    • For workloads known/suspected to call AWS APIs, inspect their manifests (in Git/IaC) and running pods to see if they:
      • Rely on node IAM role (no explicit credentials, no IRSA annotations), or
      • Use static credentials (env vars, mounted secrets), or
      • Already use IRSA (service account annotated with eks.amazonaws.com/role-arn).
  2. Confirm OIDC provider and IRSA capability for the cluster
    • On any machine with AWS CLI access:
    • If this returns None or empty, IRSA is not enabled; plan to create an OIDC provider (via console/CLI/IaC for this cluster) before proceeding with per-service-account changes.
  3. Review IAM policies attached to worker node roles for overbroad AWS access
    • On any machine with AWS CLI access:
    • Examine the policy documents for permissions that should really be scoped to specific applications (for example, broad S3, DynamoDB, KMS, SQS permissions used by only a subset of pods).
  4. Design and create least-privilege IAM roles for specific service accounts (IRSA)
    • For each application needing AWS access, define a dedicated IAM policy with only required actions and resources. Example (adjust for your case) saved as app-irsa-policy.json:
    • On any machine with AWS CLI access:
    • Create an IAM role for IRSA (trust relationship must reference the cluster’s OIDC issuer and the target service account’s namespace/name). Use console/CLI/IaC to:
      • Set the trusted entity to the cluster’s OIDC provider.
      • Limit sts:AssumeRoleWithWebIdentity to the specific system:serviceaccount:<NAMESPACE>:<SERVICEACCOUNT_NAME>.
      • Attach the app’s IAM policy created above.
  5. Update Kubernetes service accounts and workloads to use IRSA and remove legacy credential paths
    • In Git/IaC (preferred) or via console, for each target workload:
      • Create or modify its Kubernetes ServiceAccount to add the IRSA role annotation:
      • Ensure pods use this service account (spec.serviceAccountName).
      • Remove any hard-coded AWS access keys (Secrets, env vars, config files) and stop relying on the node role for those permissions.
    • After deployment, confirm the annotation is present:
  6. Reduce overprivileged node IAM permissions and verify IRSA use
    • Once all relevant workloads are successfully using IRSA (and functionally tested), on any machine with AWS CLI access:
      • Detach or narrow policies from node roles that are no longer needed by all pods:
    • Validate credential isolation and least privilege:
      • From a pod that should have IRSA, check the AWS identity:
        Confirm the ARN matches the dedicated IRSA role.
      • From a pod that should not have AWS access, run the same command and confirm it fails or uses a different, appropriate role.
kubectl can’t configure IAM roles for service accounts or IRSA; this must be implemented in the AWS/IaC layer (EKS cluster, IAM roles, and OIDC provider) using the AWS console, CLI, or infrastructure-as-code. Refer to the Manual Steps section for how to review current usage and migrate workloads to dedicated IRSA-based service accounts.
The above jq is too complex to maintain inline; instead use a safer two-step join: