Clusters Are Created With Private Endpoint Enabled And Public Access Disabled
More Info:
Disable access to the Kubernetes API from outside the node network if it is not required.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- AWS Startup Security Baseline
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS EKS
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Review current endpoint configuration (CLI)
- On any machine with AWS CLI access and appropriate IAM permissions, run:
aws eks describe-cluster --region REGION --name CLUSTER_NAME \--query 'cluster.resourcesVpcConfig.{endpointPrivateAccess:endpointPrivateAccess,endpointPublicAccess:endpointPublicAccess,publicAccessCidrs:publicAccessCidrs,subnetIds:subnetIds,securityGroupIds:securityGroupIds}' \--output json
- Record whether
endpointPrivateAccessistrue,endpointPublicAccessisfalse, and whether anypublicAccessCidrsare configured.
- On any machine with AWS CLI access and appropriate IAM permissions, run:
-
Confirm business and operational requirements for API access
- With your platform / app teams, determine:
- Whether any users, CI/CD systems, or tools need to reach the cluster API from the public internet.
- Whether all API clients can instead run from within the VPC (e.g., via bastion, VPN, Direct Connect, or VPC Peering).
- If public access is strictly required, define the minimal set of trusted IP CIDR ranges needed.
- With your platform / app teams, determine:
-
Decide the target configuration
- Preferred (strongly recommended):
endpointPrivateAccess=trueendpointPublicAccess=false
- If you must retain limited public access (risk accepted):
endpointPrivateAccess=trueendpointPublicAccess=truepublicAccessCidrsrestricted to the minimal trusted IPs (no0.0.0.0/0).
- Preferred (strongly recommended):
-
Update the cluster endpoint configuration (if change is needed)
- For fully private access only (no public access):
aws eks update-cluster-config \--region REGION \--name CLUSTER_NAME \--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=false
- To keep restricted public access (not benchmark-compliant but sometimes necessary):
aws eks update-cluster-config \--region REGION \--name CLUSTER_NAME \--resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=true,publicAccessCidrs='["X.X.X.X/32","Y.Y.Y.Y/32"]'
- Ensure you have working private connectivity (VPN/Direct Connect/bastion) before disabling public access to avoid locking out administrators and automation.
- For fully private access only (no public access):
-
Verify the new configuration (CLI)
- After the update completes, re-run:
aws eks describe-cluster --region REGION --name CLUSTER_NAME \--query 'cluster.resourcesVpcConfig.{endpointPrivateAccess:endpointPrivateAccess,endpointPublicAccess:endpointPublicAccess,publicAccessCidrs:publicAccessCidrs}' \--output json
- Confirm it matches your decision in step 3 (for benchmark compliance:
endpointPrivateAccess=trueandendpointPublicAccess=false).
- After the update completes, re-run:
-
Document the decision and any exceptions
- Record the final settings, rationale (especially if public access is retained), affected CIDRs, and any compensating controls (VPN, IP allow-lists, monitoring).
- If not fully compliant, track this as a risk acceptance with an owner and review date.
Using kubectl
kubectl cannot change EKS cluster endpoint access, because this setting is part of the managed control plane configuration in AWS, not a Kubernetes API object. To address this finding, update the cluster’s endpoint settings via the AWS console/CLI/IaC as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
#
# Report EKS cluster endpoint exposure for a list of clusters.
# Requires: aws CLI with EKS permissions, jq
#
# Usage:
# ./check_eks_api_endpoint.sh us-east-1 cluster-1 cluster-2 ...
# or:
# AWS_REGION=us-east-1 ./check_eks_api_endpoint.sh cluster-1 cluster-2 ...
#
# Any row where endpointPublicAccess is "true" is a potential problem.
# Also review endpointPublicAccessCidrs when public access is enabled.
set -euo pipefail
if ! command -v aws >/dev/null 2>&1; then
echo "ERROR: aws CLI not found in PATH" >&2
exit 1
fi
if ! command :: jq >/dev/null 2>&1; then
echo "ERROR: jq not found in PATH" >&2
exit 1
fi
REGION="${AWS_REGION:-}"
ARGS=("$@")
# Allow region as first positional arg
if [[ -z "$REGION" ]]; then
if [[ "${#ARGS[@]}" -lt 2 ]]; then
echo "Usage: $0 <aws-region> <cluster-name> [cluster-name...]" >&2
echo " or: AWS_REGION=<aws-region> $0 <cluster-name> [cluster-name...]" >&2
exit 1
fi
REGION="${ARGS[0]}"
CLUSTERS=("${ARGS[@]:1}")
else
if [[ "${#ARGS[@]}" -lt 1 ]]; then
echo "Usage: AWS_REGION=<aws-region> $0 <cluster-name> [cluster-name...]" >&2
exit 1
fi
CLUSTERS=("${ARGS[@]}")
fi
echo "Checking EKS cluster endpoint configuration in region: $REGION" >&2
echo
# Header
printf "%-30s %-8s %-8s %-45s\n" "CLUSTER" "PrivEP" "PubEP" "PubEP_CIDRs"
printf "%-30s %-8s %-8s %-45s\n" "------------------------------" "------" "------" "---------------------------------------------"
for CLUSTER in "${CLUSTERS[@]}"; do
# Describe cluster
OUT="$(aws eks describe-cluster --region "$REGION" --name "$CLUSTER" 2>/dev/null || true)"
if [[ -z "$OUT" ]]; then
printf "%-30s %-8s %-8s %-45s\n" "$CLUSTER" "ERROR" "ERROR" "cluster not found or access denied"
continue
fi
PRIV_EP="$(echo "$OUT" | jq -r '.cluster.resourcesVpcConfig.endpointPrivateAccess')"
PUB_EP="$(echo "$OUT" | jq -r '.cluster.resourcesVpcConfig.endpointPublicAccess')"
PUB_EP_CIDRS="$(echo "$OUT" | jq -r '.cluster.resourcesVpcConfig.publicAccessCidrs | join(",")')"
# Normalize empty
[[ "$PUB_EP_CIDRS" == "null" ]] && PUB_EP_CIDRS=""
printf "%-30s %-8s %-8s %-45s\n" "$CLUSTER" "$PRIV_EP" "$PUB_EP" "$PUB_EP_CIDRS"
done
cat <<'EOF'
Interpretation:
- Desired (per CIS EKS 5.4.2):
PrivEP = true
PubEP = false
- Potential problem:
Any cluster where PubEP = true
-> API is reachable from outside the VPC. Review PubEP_CIDRs:
- 0.0.0.0/0 is high risk (exposed to the internet)
- Broad CIDRs (e.g. /16, /12) may be too permissive
- Also review clusters where PrivEP = false, as node-to-API traffic is not restricted to the VPC.
This script only reports state; updating endpoint access must be done manually
via aws eks update-cluster-config or equivalent IaC/console changes.
EOF
Run location: any machine with AWS CLI access to the EKS control planes.
Problem indicators in the output:
PubEPistruefor any cluster.- Especially problematic when
PubEPistrueandPubEP_CIDRsincludes0.0.0.0/0or other very broad CIDRs. - Also review cases where
PrivEPisfalse, as they do not meet the “private endpoint enabled” intent.