┌─────────────────────────────────────────────────────────────┐
│ Credential Access by Method │
├─────────────────────────────────────────────────────────────┤
│ │
│ Method: Instance Profile (on node) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Node Instance Role │ │
│ │ │ │ │
│ │ ├──► Pod A (can access if knows role) │ │
│ │ ├──► Pod B (can access if knows role) │ │
│ │ └──► Pod C (can access if knows role) │ │
│ └─────────────────────────────────────────────────────┘ │
│ Problem: All pods on node can potentially access creds │
│ │
│ Method: Pod Identity / IRSA │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Pod A ──► Own Role (read-s3) │ │
│ │ Pod B ──► Own Role (write-dynamo) │ │
│ │ Pod C ──► Own Role (admin-eks) │ │
│ └─────────────────────────────────────────────────────┘ │
│ Benefit: Each pod has only its own permissions │
│ │
└─────────────────────────────────────────────────────────────┘
Node IMDS Consideration
IMDS Configuration
Pod can access node role?
Pod can use IRSA/Pod Identity?
IMDSv2 required
No
Yes
IMDSv1 allowed
Potentially
Yes
Unrestricted
Yes
SDK prefers IRSA
Recommendation: Always require IMDSv2:
# On all nodesaws ec2 modify-instance-metadata-options \ --instance-id i-xxxx \ --http-tokens required \ --http-put-response-hop-limit 1
IRSA vs Pod Identity Quick Reference
Aspect
IRSA
Pod Identity
Setup
Create OIDC provider + trust policy
Create EKS association
OIDC provider
Must create and manage
Not needed
Trust policy
oidc.eks.region.../id/CLUSTER_ID:sub=...
Service: pods.eks.amazonaws.com
Per-cluster config
Yes, separate trust policy
No, same role works everywhere
Cross-cluster
Requires separate roles
Single role works
SDK calls
Each pod calls STS
Cached at node level
Key rotation
7 days
Managed by EKS
Agent
None
DaemonSet required
Limits
None
5,000 associations/cluster
Fargate
Not supported
Not supported
Decision Tree
Do you need AWS SDK access from pods?
│
Yes
│
┌───────────────┴───────────────┐
│ │
Multi-cluster? Single cluster?
│ │
│ │
Use Pod Identity ┌───────────┴───────────┐
(same role works │ │
everywhere) │ │
│ │
Simpler setup? Complex OIDC needed?
│ │
│ │
Use Pod Identity Use IRSA
Kubernetes uses NodeAuthorizer to authorize kubelet requests:
# Nodes get these groups via aws-auth or Cluster Access APIgroups: - system:bootstrappers # Allows node registration - system:nodes # Allows kubelet to operate
Access entries grant IAM principals access, but RBAC controls what they can do:
IAM Principal (via Cluster Access API)
│
▼
RBAC Role/ClusterRoleBinding
│
▼
Kubernetes Permissions
Security Best Practices
1. Use IRSA or Pod Identity for All Pods
# Check for pods WITHOUT IRSA/Pod Identitykubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.serviceAccountName}{"\n"}{end}' | \ while read ns sa; do if ! kubectl get sa $sa -n $ns -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com/role-arn}' 2>/dev/null | grep -q .; then echo "No IRSA: $ns/$sa" fi done
2. Restrict IMDS Access
# Require IMDSv2 on all instancesaws ec2 modify-instance-metadata-options \ --instance-id i-xxxx \ --http-tokens required \ --http-put-response-hop-limit 1
# Bad: Same SA for multiple apps with different permissions---apiVersion: v1kind: ServiceAccountmetadata: name: shared-sa # Used by app A, B, C---# Good: Separate SAs per app---apiVersion: v1kind: ServiceAccountmetadata: name: app-a-sa # Only for app A---apiVersion: v1kind: ServiceAccountmetadata: name: app-b-sa # Only for app B
Common Patterns
Pattern 1: App with S3 Access
# 1. Create role with S3 policyaws iam create-role --role-name app-s3-role \ --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"pods.eks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'# 2. Create Pod Identity associationaws eks create-pod-identity-association \ --cluster-name my-cluster \ --namespace production \ --service-account my-app \ --role-arn arn:aws:iam::123456789:role/app-s3-role# 3. Use in deploymentkubectl apply -f - <<EOFapiVersion: v1kind: ServiceAccountmetadata: name: my-app namespace: production---apiVersion: apps/v1kind: Deploymentspec: template: spec: serviceAccountName: my-appEOF
Pattern 2: App with DynamoDB Access
# Role with DynamoDB policyaws iam create-role --role-name app-dynamo-role \ --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"pods.eks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'aws iam attach-role-policy --role-name app-dynamo-role \ --policy-arn arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess# Associationaws eks create-pod-identity-association \ --cluster-name my-cluster \ --namespace backend \ --service-account api-service \ --role-arn arn:aws:iam::123456789:role/app-dynamo-role
Pattern 3: Database App with Secrets Manager
# Role for Secrets Manager + RDSaws iam create-role --role-name db-app-role \ --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"pods.eks.amazonaws.com"},"Action":"sts:AssumeRole"}]}'# Attach policiesaws iam attach-role-policy --role-name db-app-role \ --policy-arn arn:aws:iam::aws:policy/secretsmanager:ReadWriteaws iam attach-role-policy --role-name db-app-role \ --policy-arn arn:aws:iam::aws:policy/AmazonRDSReadOnlyAccess
Anti-Patterns to Avoid
1. Using Node Role for Application Workloads
# DON'T: Pod accessing AWS resources using node role# (via IMDS if not restricted)# DO: Use IRSA or Pod Identity instead
2. Overly Broad Trust Policies
// DON'T: Allow any service account in any cluster{ "Principal": {"Federated": "*"}, "Condition": {"StringLike": {"*:sub": "system:serviceaccount:*:*"}}}// DO: Be specific{ "Condition": { "StringEquals": { "oidc.eks.us-west-2.amazonaws.com/id/CLUSTER_ID:sub": "system:serviceaccount:production:my-app" } }}
3. Using Long-Lived AWS Credentials in Code
# DON'T: Embed credentials in imageRUN aws configure set aws_access_key_id xxx# DO: Use IRSA/Pod Identity at runtime
4. Sharing Service Accounts Across Applications
# DON'T: App A and App B share same SA but have different permission needs# DO: Separate SAs with appropriate permissions for each app