Amazon EKS (Elastic Kubernetes Service) provides managed Kubernetes clusters, but security remains a shared responsibility. This comprehensive guide covers hardening EKS clusters from network configuration to pod security, ensuring your containerized workloads are protected against modern threats.
EKS security encompasses multiple layers: the control plane managed by AWS, the data plane (worker nodes) you manage, and the workloads running on those nodes. Each layer requires specific security controls and configurations to achieve defense in depth.
EKS Control Plane Security
AWS manages the Kubernetes control plane, including etcd encryption, API server availability, and automatic patching. However, you control access to the API server through endpoint configuration and authentication settings.
resource "aws_eks_cluster" "main" {
name = "production-cluster"
role_arn = aws_iam_role.cluster.arn
version = "1.28"
vpc_config {
subnet_ids = var.private_subnet_ids
endpoint_private_access = true
endpoint_public_access = false
security_group_ids = [aws_security_group.cluster.id]
}
encryption_config {
provider { key_arn = aws_kms_key.eks.arn }
resources = ["secrets"]
}
enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
}Node Group Security
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "production-nodes"
node_role_arn = aws_iam_role.node.arn
subnet_ids = var.private_subnet_ids
instance_types = ["m5.large"]
launch_template {
id = aws_launch_template.nodes.id
version = aws_launch_template.nodes.latest_version
}
scaling_config {
desired_size = 3
max_size = 10
min_size = 2
}
}
resource "aws_launch_template" "nodes" {
name = "eks-nodes"
metadata_options {
http_endpoint = "enabled"
http_tokens = "required" # IMDSv2 required
http_put_response_hop_limit = 1
}
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 100
volume_type = "gp3"
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
delete_on_termination = true
}
}
}IRSA (IAM Roles for Service Accounts)
resource "aws_iam_role" "app_role" {
name = "eks-app-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Federated = aws_iam_openid_connect_provider.eks.arn }
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub" = "system:serviceaccount:production:app-sa"
}
}
}]
})
}Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedNetwork Policies with Calico
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressSecrets Encryption
resource "aws_kms_key" "eks" {
description = "EKS Secret Encryption Key"
deletion_window_in_days = 30
enable_key_rotation = true
}GuardDuty EKS Protection
resource "aws_guardduty_detector" "main" {
enable = true
datasources {
kubernetes { audit_logs { enable = true } }
}
}Best Practices
- Use private endpoint access only when possible
- Enable envelope encryption for secrets with KMS
- Implement IRSA for pod-level IAM permissions
- Enforce Pod Security Standards at namespace level
- Deploy network policies with Calico or Cilium
- Enable GuardDuty EKS protection
- Use managed node groups with IMDSv2 required
- Encrypt EBS volumes with customer-managed KMS keys
- Enable all control plane logging
- Regularly update cluster and node versions
Conclusion
EKS security requires a layered approach covering the control plane, worker nodes, and workloads. By implementing these best practices, you create a secure foundation for running containerized applications in production.

