Secure AWS Access in Kubernetes: Transitioning from Secrets to IRSA or Pod Identity

Traditional approaches to AWS authorization from Kubernetes involved creating IAM users with permanent access keys and storing these credentials within the cluster. Modern solutions eliminate stored credentials entirely, using temporary tokens that expire automatically and follow the principle of least privilege. Think of it like upgrading from physical keys to smart cards for your Kubernetes workloads - instead of storing a master key in every pod, your containers get temporary access cards that only work for specific resources and expire automatically.
The old way: stored credentials (what to avoid)
Before diving into modern solutions, let's understand what we're moving away from. Traditional approaches to AWS access from Kubernetes typically involved:
- Create IAM users with access keys (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)
- Store these credentials in Kubernetes secrets, environment variables, or config files
- Hardcode them in container images or application code
Here's what this looked like in a typical Kubernetes deployment:
apiVersion: v1 kind: Secret metadata: name: aws-credentials namespace: default type: Opaque data: AWS_ACCESS_KEY_ID: ABCDEF AWS_SECRET_ACCESS_KEY: GHIJKL --- apiVersion: apps/v1 kind: Deployment metadata: name: legacy-app namespace: default spec: replicas: 2 selector: matchLabels: app: legacy-app template: metadata: labels: app: legacy-app spec: containers: - name: legacy-app image: my-app:latest env: - name: AWS_ACCESS_KEY_ID valueFrom: secretKeyRef: name: aws-credentials key: AWS_ACCESS_KEY_ID - name: AWS_SECRET_ACCESS_KEY valueFrom: secretKeyRef: name: aws-credentials key: AWS_SECRET_ACCESS_KEY - name: AWS_REGION value: "us-west-2"
This approach creates several security and operational challenges in Kubernetes environments:
Security risks:
- Long-lived credentials don't expire automatically
- Keys can be extracted from running containers or Kubernetes secrets
- Credential leaks provide persistent access until manually revoked
- Difficult to audit credential usage across pods and namespaces
Operational challenges:
- Manual key rotation across all pods and clusters
- Credential sprawl as keys get copied across namespaces and environments
- Complex secret management across multiple Kubernetes clusters
Modern authorization methods for Kubernetes
Modern AWS authorization from Kubernetes eliminates stored credentials by using temporary tokens that are automatically refreshed. Two primary solutions exist for Kubernetes-to-AWS authentication:
IRSA (IAM roles for service accounts)
IRSA uses OpenID Connect (OIDC) to bridge Kubernetes service accounts with AWS IAM roles. When a pod needs AWS access, it presents a JWT token that AWS validates through the cluster's OIDC provider, receiving temporary credentials in return.
How it works:
- Pod receives a JWT token from the Kubernetes API server automatically
- AWS SDK includes this token in API requests
- AWS STS validates the token through the EKS cluster's OIDC provider
- STS returns temporary AWS credentials to the pod
- Pod uses these credentials to access AWS services
EKS pod identity
EKS Pod Identity is AWS's newer approach, using a cluster add-on that handles credential exchange automatically. A Pod Identity agent runs as a DaemonSet on each Kubernetes node, intercepting AWS API calls and fetching credentials from AWS's Pod Identity service.
How it works:
- Pod makes AWS API calls through the AWS SDK
- Pod Identity agent (running as DaemonSet) intercepts these calls
- Agent requests credentials from the EKS Pod Identity service
- Service returns temporary credentials to the agent
- Original API call proceeds with valid credentials
Implementation with Terraform
Let's implement both approaches using consistent IAM policies. We'll use a common S3 access policy for both examples that pods can use to access AWS services:
resource "aws_iam_policy" "s3_access" { name = "kubernetes-app-s3-access" description = "S3 access policy for Kubernetes applications" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ] Resource = "arn:aws:s3:::my-k8s-app-bucket/*" }, { Effect = "Allow" Action = [ "s3:ListBucket" ] Resource = "arn:aws:s3:::my-k8s-app-bucket" } ] }) }
Setting up IRSA for EKS
Configure your EKS cluster with OIDC enabled to support IRSA:
module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 19.0.0" cluster_name = "my-k8s-cluster" cluster_version = "1.28" # create an OpenID Connect Provider for EKS to enable IRSA (IAM Roles for Service Accounts) enable_irsa = true vpc_id = var.vpc_id subnet_ids = var.private_subnets eks_managed_node_groups = { main = { desired_size = 2 max_size = 4 min_size = 1 instance_types = ["t3.medium"] } } }
Create the IAM role that your Kubernetes service account will assume:
module "irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "k8s-app-irsa-role" role_policy_arns = { s3_access = aws_iam_policy.s3_access.arn } oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["default:k8s-app-service-account"] } } }
Setting up EKS pod identity
Enable the Pod Identity add-on in your EKS cluster:
module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 19.0.0" cluster_name = "my-k8s-cluster" cluster_version = "1.28" cluster_addons = { eks-pod-identity-agent = { addon_version = "v1.3.4-eksbuild.1" } } vpc_id = var.vpc_id subnet_ids = var.private_subnets eks_managed_node_groups = { main = { desired_size = 2 max_size = 4 min_size = 1 instance_types = ["t3.medium"] } } }
Kubernetes manifests
IRSA application deployment
apiVersion: v1 kind: ServiceAccount metadata: name: k8s-app-service-account namespace: default annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/k8s-app-irsa-role --- apiVersion: apps/v1 kind: Deployment metadata: name: k8s-app-irsa namespace: default spec: replicas: 2 selector: matchLabels: app: k8s-app-irsa template: metadata: labels: app: k8s-app-irsa spec: serviceAccountName: k8s-app-service-account containers: - name: app image: my-app:latest env: - name: AWS_REGION value: "us-west-2" ports: - containerPort: 8080
Pod identity application deployment
apiVersion: v1 kind: ServiceAccount metadata: name: k8s-app-service-account namespace: default --- apiVersion: apps/v1 kind: Deployment metadata: name: k8s-app-pod-identity namespace: default spec: replicas: 2 selector: matchLabels: app: k8s-app-pod-identity template: metadata: labels: app: k8s-app-pod-identity spec: serviceAccountName: k8s-app-service-account containers: - name: app image: my-app:latest env: - name: AWS_REGION value: "us-west-2" ports: - containerPort: 8080
Notice how both modern approaches eliminate the need for stored credentials entirely in your Kubernetes cluster. The Pod Identity deployment is even simpler, requiring no special annotations on the service account.
Comparing modern approaches for Kubernetes
IRSA strengths and limitations
Strengths:
- Works with any Kubernetes cluster, not just EKS
- Mature technology with extensive community support
- Fine-grained control over JWT token configuration
- Compatible with other OIDC-based systems and service meshes
Limitations:
- More complex setup and troubleshooting in Kubernetes
- Requires OIDC provider configuration at the cluster level
- JWT tokens can become large and cause issues with some applications
- Manual annotation management required for each service account
Pod identity strengths and limitations
Strengths:
- Simplified setup and ongoing management in EKS
- Native AWS integration with better performance
- Automatic credential refresh and rotation handled by the agent
- Built-in audit logging capabilities through CloudTrail
- No JWT token size limitations or complexity
Limitations:
- EKS-only solution, not portable to other Kubernetes distributions
- Newer technology with less community knowledge and troubleshooting guides
- Requires EKS cluster version 1.24 or higher
- Less customization flexibility compared to OIDC-based approaches
Security best practices for Kubernetes workloads
Regardless of which modern approach you choose for your Kubernetes cluster, follow these security principles:
Principle of least privilege:
# Good: Specific permissions for specific resources used by pods resource "aws_iam_policy" "restricted_s3" { policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["s3:GetObject"] Resource = "arn:aws:s3:::k8s-specific-bucket/app-folder/*" } ] }) } # Avoid: Overly broad permissions that pods don't need resource "aws_iam_policy" "too_broad" { policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["s3:*"] Resource = "*" } ] }) }
Additional security measures for Kubernetes:
- Use Kubernetes namespace isolation to limit service account scope
- Implement regular auditing of role usage and permissions across clusters
- Monitor credential usage through AWS CloudTrail and Kubernetes audit logs
- Set up alerts for unusual access patterns from pods
- Use Kubernetes Network Policies to restrict pod-to-pod communication
- Regularly review and rotate any remaining long-lived credentials in your cluster
Choosing the right approach
Choose IRSA when:
- You need multi-cloud or hybrid Kubernetes support
- You have complex OIDC integration requirements
- Your team has existing IRSA expertise
- You need fine-grained token control
Choose Pod Identity when:
- You're using EKS exclusively
- You want simplified management overhead
- You're starting new projects from scratch
- You prefer AWS-native solutions
Migrate from stored credentials when:
- You're currently using long-lived access keys
- You want to improve security posture
- You need better credential rotation
- You want to reduce operational overhead
Conclusion
Modern AWS authorization methods eliminate the security risks and operational complexity of stored credentials in Kubernetes environments. Both IRSA and EKS Pod Identity provide secure, temporary credential access that follows cloud security best practices while integrating seamlessly with Kubernetes workflows.
For new EKS projects, Pod Identity offers the simplest path forward with native AWS integration and reduced management overhead. For existing Kubernetes environments or multi-platform requirements, IRSA provides proven reliability and flexibility across different cluster types.
The most important step is moving away from stored credentials entirely in your Kubernetes clusters. Choose the modern approach that best fits your team's expertise and infrastructure requirements - both represent significant improvements over traditional credential management and will enhance your cluster's security posture while simplifying operations.