Restrict Access To The Control Plane Endpoint
More Info:
Enable private endpoint access to the Kubernetes API server and restrict public access to specific CIDR blocks so the control plane is not exposed to all IP addresses.
Risk Level
High
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Gather current endpoint configuration (any machine with AWS CLI access)
aws eks describe-cluster \--region us-east-1 \--name my-eks-cluster \--query "cluster.resourcesVpcConfig.{endpointPrivateAccess:endpointPrivateAccess,endpointPublicAccess:endpointPublicAccess,publicAccessCidrs:publicAccessCidrs}" \--output jsonReview:
endpointPrivateAccessshould betrue.- If
endpointPublicAccessistrue, ensurepublicAccessCidrsis a small, explicit list of trusted CIDRs (ideally /32 for admin IPs or narrow corporate ranges), not["0.0.0.0/0"]and not empty.
-
Decide the desired exposure model (design decision, no command)
Choose one of:- Private-only:
endpointPrivateAccess=true,endpointPublicAccess=false; access via VPC/VPN/Direct Connect/bastion. - Private + restricted public:
endpointPrivateAccess=true,endpointPublicAccess=true, andpublicAccessCidrsset to only necessary admin IPs or ranges.
Confirm that the networking team can reach the API via VPC paths if you disable or significantly restrict public access.
- Private-only:
-
Update to private-only endpoint if public access is not required (any machine with AWS CLI access)
aws eks update-cluster-config \--region us-east-1 \--name my-eks-cluster \--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=falseBe aware: turning off public access immediately cuts off internet-based kubectl/API access; ensure admins have connectivity via the VPC first.
-
Update to private + restricted public access if limited internet access is required (any machine with AWS CLI access)
Replace the CIDRs with your approved admin IPs/ranges (must not include reserved addresses):aws eks update-cluster-config \--region us-east-1 \--name my-eks-cluster \--resources-vpc-config \endpointPrivateAccess=true,endpointPublicAccess=true,publicAccessCidrs="198.51.100.10/32,203.0.113.0/24"Coordinate with stakeholders so only documented, maintained CIDRs are allowed; avoid broad ranges (e.g. /0, /8).
-
Align IaC / automation with the chosen configuration (Git/IaC system, no AWS command)
- For CloudFormation/CDK/Terraform/etc., set:
- Private endpoint enabled.
- Public endpoint disabled, or enabled with the exact
publicAccessCidrsdecided in steps 2 and 4.
Ensure future deployments/updates cannot revert to0.0.0.0/0or an emptypublicAccessCidrs.
- For CloudFormation/CDK/Terraform/etc., set:
-
Verify final state matches the decision (any machine with AWS CLI access)
aws eks describe-cluster \--region us-east-1 \--name my-eks-cluster \--query "cluster.resourcesVpcConfig" \--output jsonConfirm:
endpointPrivateAccessistrue.endpointPublicAccessandpublicAccessCidrsexactly match the chosen model in step 2.
Using kubectl
kubectl cannot modify the EKS control plane endpoint exposure or its allowed CIDR blocks; those settings are managed in the AWS control plane via the AWS Console, AWS CLI, or IaC. Use kubectl only to inspect cluster state, and follow the guidance in the Manual Steps section to update the endpoint configuration.
Automation
#!/usr/bin/env bash
#
# audit-eks-endpoint-access.sh
#
# Purpose:
# Report EKS API endpoint exposure for all clusters in one or more regions.
# This does NOT change anything; it only reports for manual review.
#
# Requirements:
# - aws CLI v2 configured with sufficient permissions:
# eks:ListClusters
# eks:DescribeCluster
#
# Run on:
# - Any machine with aws CLI access (does NOT require kubectl).
set -euo pipefail
REGIONS="${*:-}"
if [ -z "$REGIONS" ]; then
# Auto-discover all regions with EKS service available
REGIONS="$(aws ec2 describe-regions --query 'Regions[].RegionName' --output text)"
fi
printf "Auditing EKS cluster endpoint exposure\n" >&2
printf "Regions: %s\n\n" "$REGIONS" >&2
# Header
printf "%-15s %-40s %-8s %-8s %-40s %s\n" \
"REGION" "CLUSTER" "PRIV" "PUB" "PUBLIC_CIDRS" "RISK_INDICATOR"
for region in $REGIONS; do
clusters=$(aws eks list-clusters --region "$region" --output text --query 'clusters[]' || true)
if [ -z "$clusters" ]; then
continue
fi
for cluster in $clusters; do
desc_json=$(aws eks describe-cluster --region "$region" --name "$cluster" --output json)
priv_access=$(echo "$desc_json" | jq -r '.cluster.resourcesVpcConfig.endpointPrivateAccess')
pub_access=$(echo "$desc_json" | jq -r '.cluster.resourcesVpcConfig.endpointPublicAccess')
pub_cidrs=$(echo "$desc_json" | jq -r '.cluster.resourcesVpcConfig.publicAccessCidrs | join(",")')
# Default if field is empty (API may omit, but default is 0.0.0.0/0)
if [ -z "$pub_cidrs" ] || [ "$pub_cidrs" = "null" ]; then
pub_cidrs="0.0.0.0/0 (IMPLICIT DEFAULT)"
fi
risk="OK"
# Problem indicators per benchmark intent:
# 1) Public endpoint open to the world.
# 2) Public endpoint enabled with overly-broad ranges.
# 3) No private endpoint.
if [ "$pub_access" = "true" ]; then
# Normalize for checks
cidrs_clean=$(echo "$pub_cidrs" | tr ',' '\n' | sed 's/ (IMPLICIT DEFAULT)//g')
world_open=false
overly_broad=false
# Flag '0.0.0.0/0' as open to world
if echo "$cidrs_clean" | grep -qE '^0\.0\.0\.0/0$'; then
world_open=true
fi
# Flag common broad ranges (example heuristics)
if echo "$cidrs_clean" | grep -qE '(^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.)'; then
overly_broad=true
fi
if $world_open; then
risk="HIGH: Public endpoint open to the world (0.0.0.0/0)"
elif $overly_broad; then
risk="MEDIUM: Public endpoint CIDR(s) appear broad; review"
else
risk="REVIEW: Public endpoint restricted; verify IP list is minimal/justified"
fi
fi
if [ "$priv_access" != "true" ]; then
if [ "$risk" = "OK" ]; then
risk="REVIEW: Private endpoint disabled; consider enabling"
else
risk="$risk; Private endpoint disabled"
fi
fi
printf "%-15s %-40s %-8s %-8s %-40s %s\n" \
"$region" "$cluster" "$priv_access" "$pub_access" "$pub_cidrs" "$risk"
done
done
How to run (any machine with AWS CLI access):
# All regions
bash audit-eks-endpoint-access.sh
# Specific regions
bash audit-eks-endpoint-access.sh us-east-1 us-west-2
How to interpret the output:
-
PRIV=trueandPUB=false- Generally aligned with “private endpoint only”; review still recommended but low concern.
-
RISK_INDICATORcontains:HIGH: Public endpoint open to the world (0.0.0.0/0)- Fails the intent of the control; public API is exposed to all IPs.
MEDIUM: Public endpoint CIDR(s) appear broad; review- Likely not minimal; ranges look like internal-wide or large networks.
REVIEW: Public endpoint restricted; verify IP list is minimal/justified- Public access is limited, but you must manually confirm those CIDRs are specific, necessary, and documented.
...; Private endpoint disabled- Private endpoint not enabled; consider enabling to keep node–API traffic inside the VPC.
Use these results as an input to a manual review and policy decision; do not assume that any non-HIGH status is automatically compliant without human validation.