Skip to main content

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

Manual Steps
  1. Identify clusters and current endpoint exposure

    • On any machine with AWS CLI access:
      aws eks list-clusters --region us-east-1
      aws 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 private is true, public is false, and whether any CIDRs are allowed.
  2. 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 publicAccessCidrs instead of fully disabling public access.
  3. 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.
  4. 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.
  5. Verify the configuration and connectivity

    • Confirm endpoint flags:
      aws eks describe-cluster \
      --region us-east-1 \
      --name <cluster-name> \
      --query "cluster.resourcesVpcConfig"
      Ensure "endpointPrivateAccess": true and "endpointPublicAccess": false (or, if intentionally retained, true with minimal publicAccessCidrs).
    • From a bastion or admin host inside the VPC, verify kubectl still works:
      kubectl --context <cluster-context> get nodes
  6. Document the decision and residual risk

    • Record for each cluster:
      • Final values of endpointPrivateAccess, endpointPublicAccess, and publicAccessCidrs.
      • The rationale if public access is not fully disabled.
      • Any compensating controls (IP restrictions, strong auth, logging).
    • Revisit this decision periodically and after major network/organizational changes.
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