Amazon EKS — Private Cluster Access
In production, you should restrict who can talk to your EKS API server. EKS supports three API server endpoint access modes. Choosing the right one is one of the most important security decisions you’ll make.
API Server Endpoint Access Modes
| Mode | Public | Private | Who Can Reach the API |
|---|---|---|---|
| Public (default) | Yes | No | Anyone on the internet (filtered by CIDR) |
| Public + Private | Yes | Yes | Internet + nodes within VPC use private endpoint |
| Private | No | Yes | Only from within the VPC or peered/connected networks |
How Each Mode Works
Public Endpoint (Default)
Internet ──→ Public API Endpoint ──→ EKS Control Plane
│
Nodes ──→ Public API Endpoint ────────────┘
(via NAT Gateway)
- API server is accessible from the internet
- Worker nodes reach the API via NAT Gateway → internet → public endpoint
- You can restrict by CIDR (e.g., only your office IP)
- Not recommended for production — nodes shouldn’t route control plane traffic through the internet
Public + Private Endpoint
Internet ──→ Public API Endpoint ──→ EKS Control Plane
│
Nodes ──→ Private API Endpoint ──────────┘
(via VPC ENIs, no internet)
- External access is still available (for your laptop, CI/CD)
- Nodes communicate through the private endpoint (within VPC)
- Node traffic never leaves the VPC
- Good compromise for most production environments
Private Endpoint Only
Internet ──✗ (blocked)
VPN/Direct Connect ──→ Private API Endpoint ──→ EKS Control Plane
│
Nodes ──→ Private API Endpoint ───────────────────────┘
- API server is only accessible from within the VPC
- You need VPN, Direct Connect, or a bastion host to access the cluster
- Most secure option
- Recommended for high-security environments
Configuring Endpoint Access
Using AWS CLI
# Public + Private (recommended for most)
aws eks update-cluster-config \
--name my-cluster \
--resources-vpc-config \
endpointPublicAccess=true,endpointPrivateAccess=true,publicAccessCidrs=["203.0.113.0/24"]
# Private only
aws eks update-cluster-config \
--name my-cluster \
--resources-vpc-config \
endpointPublicAccess=false,endpointPrivateAccess=true
Using eksctl
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: my-cluster
region: us-east-1
vpc:
clusterEndpoints:
publicAccess: true
privateAccess: true
publicAccessCIDRs:
- "203.0.113.0/24" # Your office IP
- "198.51.100.0/24" # VPN egress IP
Using Terraform
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "my-cluster"
cluster_version = "1.29"
cluster_endpoint_public_access = true
cluster_endpoint_private_access = true
cluster_endpoint_public_access_cidrs = [
"203.0.113.0/24", # Office IP
"198.51.100.0/24", # VPN egress
]
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
}
Private Cluster Access Patterns
When your API endpoint is private-only, you need a way to reach it. Here are the common patterns:
Pattern 1 — Bastion Host (Jump Box)
Your Laptop ──SSH──→ Bastion (public subnet) ──kubectl──→ EKS API (private)
# SSH into the bastion
ssh -i my-key.pem ec2-user@bastion-public-ip
# Run kubectl from the bastion
kubectl get nodes
Pros: Simple, well-understood Cons: Another server to manage, SSH key management, single point of access
Pattern 2 — AWS Systems Manager Session Manager
Your Laptop ──SSM──→ EC2 Instance (private subnet) ──kubectl──→ EKS API (private)
No SSH keys, no public IP, no bastion needed. SSM uses the AWS API to establish a session.
# Start a session (no SSH needed)
aws ssm start-session --target i-1234567890abcdef0
# Port forward kubectl to your laptop
aws ssm start-session \
--target i-1234567890abcdef0 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{
"host": ["ABCDEF1234.gr7.us-east-1.eks.amazonaws.com"],
"portNumber": ["443"],
"localPortNumber": ["6443"]
}'
Pros: No SSH keys, audit trail in CloudTrail, no public IP needed Cons: Requires SSM agent on EC2, slight latency
Pattern 3 — VPN (AWS Client VPN or Site-to-Site)
Your Laptop ──VPN──→ VPC ──kubectl──→ EKS API (private)
# Create Client VPN endpoint
aws ec2 create-client-vpn-endpoint \
--client-cidr-block "10.100.0.0/16" \
--server-certificate-arn arn:aws:acm:us-east-1:123456789:certificate/abc \
--authentication-options Type=certificate-authentication,MutualAuthentication={ClientRootCertificateChainArn=arn:aws:acm:us-east-1:123456789:certificate/def} \
--connection-log-options Enabled=false \
--vpc-id vpc-123456
Pros: Full VPC access from laptop, transparent to kubectl Cons: VPN setup complexity, Client VPN costs (~$0.10/hr + connection fees)
Pattern 4 — AWS Direct Connect
Corporate DC ──Direct Connect──→ VPC ──kubectl──→ EKS API (private)
Pros: Dedicated, low-latency, high-bandwidth private connection Cons: Expensive, long provisioning time (weeks/months)
Pattern 5 — VPC Peering / Transit Gateway
For CI/CD in another VPC:
CI/CD VPC ──Peering/TGW──→ EKS VPC ──→ EKS API (private)
# Create peering connection
aws ec2 create-vpc-peering-connection \
--vpc-id vpc-cicd \
--peer-vpc-id vpc-eks
# Update route tables to route EKS private endpoint traffic
aws ec2 create-route \
--route-table-id rtb-cicd \
--destination-cidr-block 10.0.0.0/16 \
--vpc-peering-connection-id pcx-123456
VPC Endpoints for Private Clusters
When the API server is private-only, nodes still need to reach AWS services for: pulling ECR images, writing CloudWatch logs, downloading EKS add-ons.
Without VPC endpoints, nodes need a NAT Gateway for internet access. VPC endpoints eliminate that dependency (and save money):
Recommended VPC Endpoints
| Endpoint | Service | Why Needed |
|---|---|---|
com.amazonaws.region.ec2 |
EC2 API | VPC CNI calls EC2 APIs |
com.amazonaws.region.ecr.api |
ECR API | Image pull authentication |
com.amazonaws.region.ecr.dkr |
ECR Docker | Image layer downloads |
com.amazonaws.region.s3 |
S3 (Gateway) | ECR image layers stored in S3 |
com.amazonaws.region.sts |
STS | IRSA / Pod Identity token exchange |
com.amazonaws.region.logs |
CloudWatch Logs | Container logging |
com.amazonaws.region.elasticloadbalancing |
ELB | AWS LB Controller |
com.amazonaws.region.autoscaling |
Auto Scaling | Cluster Autoscaler |
Creating VPC Endpoints
# Interface endpoint (ECR API)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-123456 \
--service-name com.amazonaws.us-east-1.ecr.api \
--vpc-endpoint-type Interface \
--subnet-ids subnet-private-a subnet-private-b \
--security-group-ids sg-vpc-endpoint
# Gateway endpoint (S3 — free)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-123456 \
--service-name com.amazonaws.us-east-1.s3 \
--vpc-endpoint-type Gateway \
--route-table-ids rtb-private
Cost: Interface endpoints cost ~$0.01/hr + $0.01/GB. Gateway endpoints (S3, DynamoDB) are free.
DNS Considerations for Private Clusters
When you enable the private endpoint, EKS creates a private hosted zone in Route 53 that resolves the API server hostname to private IPs within your VPC.
# From within the VPC, the API hostname resolves to private IPs
nslookup ABCDEF1234.gr7.us-east-1.eks.amazonaws.com
# Returns: 10.0.1.55, 10.0.2.66
# From outside the VPC (if public endpoint disabled), it returns nothing useful
If you’re connecting via VPN or VPC peering, ensure DNS resolution works across the connection:
# Enable DNS resolution for VPC peering
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-123456 \
--requester-peering-connection-options AllowDnsResolutionFromRemoteVpc=true
Restricting Public Access with CIDRs
If you use public + private mode, restrict the public endpoint to trusted IPs:
aws eks update-cluster-config \
--name my-cluster \
--resources-vpc-config publicAccessCidrs='["203.0.113.0/24","198.51.100.0/32"]'
Best practice: Only allow your office IP, VPN egress IPs, and CI/CD runner IPs. Never leave it open to 0.0.0.0/0.
Comparison of Access Patterns
| Pattern | Setup Complexity | Cost | Security | Best For |
|---|---|---|---|---|
| Public endpoint + CIDR restriction | Low | Free | Medium | Small teams, learning |
| Public + private + CIDR | Low | Free | Good | Most production clusters |
| Private + bastion | Medium | ~$10/month | High | Simple private access |
| Private + SSM | Medium | Free (SSM included) | High | AWS-native teams |
| Private + VPN | High | ~$75+/month | High | Multiple developers |
| Private + Direct Connect | Very High | \(\) | Very High | Enterprise, hybrid |
Best Practices
- Never use public-only endpoint in production — at minimum, enable private too
- Restrict public CIDRs — don’t leave the public endpoint open to the world
- Use VPC endpoints to avoid NAT Gateway dependency and reduce data transfer costs
- Prefer SSM Session Manager over bastion hosts — no SSH key management, built-in audit trail
- Enable CloudTrail for API server access — all
kubectlcommands are logged as API calls - Plan DNS resolution when accessing private clusters from peered VPCs or VPNs
- Consider the team workflow — developers need a smooth way to run
kubectl; don’t make it so painful they avoid using it