Clusters Are Created With Private Endpoint Enabled And
More Info:
Create clusters with the private API server endpoint enabled and public endpoint access disabled so that all communication between nodes and the API server stays within the VPC.
Risk Level
Critical
Address
Security
Compliance Standards
- CIS EKS
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify clusters and current endpoint exposure
- On any machine with AWS CLI access:
aws eks list-clusters --region us-east-1aws eks list-clusters --region us-west-2
- For each cluster:
aws eks describe-cluster \--region us-east-1 \--name <cluster-name> \--query "cluster.resourcesVpcConfig.{private:endpointPrivateAccess,public:endpointPublicAccess,publicCidrs:publicAccessCidrs}" \--output table
- Record whether
privateistrue,publicisfalse, and whether any CIDRs are allowed.
- On any machine with AWS CLI access:
-
Review access requirements and break-glass needs
- With your platform/network team, decide whether:
- All administrative/API access can occur from within the VPC via peering/VPN/Direct Connect.
- Any users or automation require public API access (for break-glass or existing workflows).
- If public access is truly unnecessary, plan to set
endpointPublicAccess=false. - If some public access is required, document the minimal set of IP CIDRs that must reach the API and prepare to restrict via
publicAccessCidrsinstead of fully disabling public access.
- With your platform/network team, decide whether:
-
Confirm network path for private-only access
- Verify that all nodes and admin entry points have a private path to the cluster’s VPC subnets:
- Check VPC peering / Transit Gateway / VPN / Direct Connect configuration.
- Confirm that security groups, route tables, and NACLs permit HTTPS (443) to the EKS control plane private endpoint from admin networks and node subnets.
- If this is not currently in place, implement and validate connectivity before disabling public access.
- Verify that all nodes and admin entry points have a private path to the cluster’s VPC subnets:
-
Update cluster endpoint configuration (if approved)
- On any machine with AWS CLI access, for a cluster where private-only is acceptable:
aws eks update-cluster-config \--region us-east-1 \--name <cluster-name> \--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=false
- If you must temporarily retain restricted public access instead of disabling it:
aws eks update-cluster-config \--region us-east-1 \--name <cluster-name> \--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=true,publicAccessCidrs=10.0.0.0/16,203.0.113.10/32
- Schedule and communicate this change; expect an impact on any clients currently reaching the public endpoint.
- On any machine with AWS CLI access, for a cluster where private-only is acceptable:
-
Verify the configuration and connectivity
- Confirm endpoint flags:
Ensureaws eks describe-cluster \--region us-east-1 \--name <cluster-name> \--query "cluster.resourcesVpcConfig"
"endpointPrivateAccess": trueand"endpointPublicAccess": false(or, if intentionally retained,truewith minimalpublicAccessCidrs). - From a bastion or admin host inside the VPC, verify
kubectlstill works:kubectl --context <cluster-context> get nodes
- Confirm endpoint flags:
-
Document the decision and residual risk
- Record for each cluster:
- Final values of
endpointPrivateAccess,endpointPublicAccess, andpublicAccessCidrs. - The rationale if public access is not fully disabled.
- Any compensating controls (IP restrictions, strong auth, logging).
- Final values of
- Revisit this decision periodically and after major network/organizational changes.
- Record for each cluster:
Using kubectl
kubectl cannot be used to enable a private EKS API endpoint or disable public access, because this setting is part of the managed control plane configuration. Make these changes via the AWS console, AWS CLI, or your IaC definitions as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
#
# Report EKS cluster endpoint exposure for multiple clusters (MANUAL review).
#
# Requirements:
# - aws CLI v2 configured with appropriate credentials
# - jq installed
#
# Usage examples:
# ./eks-endpoint-audit.sh # all regions in account
# AWS_REGION=us-east-1 ./eks-endpoint-audit.sh
# AWS_REGIONS="us-east-1 eu-west-1" ./eks-endpoint-audit.sh
#
# Interpretation (problem indicators):
# - endpointPublicAccess == true => RISK: public API endpoint is enabled
# - endpointPrivateAccess == false => RISK: no private access from VPC
# - Both conditions together => HIGHEST RISK
#
# This script ONLY reports state; changing endpoint settings must be done
# deliberately per cluster via AWS CLI/console/IaC after review.
set -euo pipefail
# Discover regions
if [[ -n "${AWS_REGIONS:-}" ]]; then
REGIONS="${AWS_REGIONS}"
elif [[ -n "${AWS_REGION:-}" ]]; then
REGIONS="${AWS_REGION}"
else
REGIONS="$(aws ec2 describe-regions --all-regions --query 'Regions[].RegionName' --output text)"
fi
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo "Account ID: $(aws sts get-caller-identity --query 'Account' --output text)"
echo "Regions: ${REGIONS}"
echo
# Header
printf "%-15s %-35s %-8s %-8s %-60s\n" \
"REGION" "CLUSTER" "PUB" "PRIV" "PUBLIC_ACCESS_CIDRS"
printf "%0.s-" {1..140}; echo
for region in ${REGIONS}; do
clusters=$(aws eks list-clusters --region "${region}" --query 'clusters[]' --output text 2>/dev/null || true)
if [[ -z "${clusters}" ]]; then
continue
fi
for cluster in ${clusters}; do
desc_json=$(aws eks describe-cluster --region "${region}" --name "${cluster}" --output json)
pub=$(echo "${desc_json}" | jq -r '.cluster.resourcesVpcConfig.endpointPublicAccess')
priv=$(echo "${desc_json}" | jq -r '.cluster.resourcesVpcConfig.endpointPrivateAccess')
cidrs=$(echo "${desc_json}" | jq -r '.cluster.resourcesVpcConfig.publicAccessCidrs | join(",")')
printf "%-15s %-35s %-8s %-8s %-60s\n" \
"${region}" "${cluster}" "${pub}" "${priv}" "${cidrs}"
done
done
cat <<'EOF'
Interpretation guidance (MANUAL review required):
- Desired (per CIS EKS 5.4.2):
PUB=false and PRIV=true
- Problematic states:
1) PUB=true and PRIV=false
- API server only reachable via public endpoint from the internet CIDRs.
- ACTION: Strongly consider switching to private-only, with controlled jump/bastion or VPN.
2) PUB=true and PRIV=true
- Private access exists, but public endpoint is still exposed.
- Review 'PUBLIC_ACCESS_CIDRS':
* 0.0.0.0/0 or very broad CIDRs => high risk; restrict or disable.
* Narrow CIDRs may be acceptable based on your risk appetite and access model.
3) PUB=false and PRIV=false
- Misconfigured / unusable state for normal operation.
- Investigate immediately; cluster access may be broken.
Decisions to make per cluster:
- Is any public endpoint exposure (PUB=true) acceptable in this environment?
- If yes, are PUBLIC_ACCESS_CIDRS tightly restricted to necessary corporate IP ranges?
- Can you operate with PRIV-only access (recommended) using VPN/Direct Connect/VPC peering/bastion?
To change settings after review (example, NOT executed by this script):
aws eks update-cluster-config \
--region <region> \
--name <cluster-name> \
--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=false
Apply changes via your standard change-management / IaC process.
EOF