Skip to main content

Restrict Access To Control Plane Endpoint

More Info:

Enable Endpoint Private Access to restrict access to the clusters control plane to only an allowlist of authorized IPs.

Risk Level

Medium

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • 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)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. Gather current cluster endpoint configuration
    Run on any machine with AWS CLI access and IAM permissions:

    aws eks describe-cluster \
    --region us-east-1 \
    --name my-eks-cluster \
    --query 'cluster.resourcesVpcConfig.{endpointPrivateAccess:endpointPrivateAccess,endpointPublicAccess:endpointPublicAccess,publicAccessCidrs:publicAccessCidrs}' \
    --output table

    Replace us-east-1 and my-eks-cluster with your actual region and cluster name.

  2. Decide desired access model with security/network teams

    • Preferred: private-only API (no public access) for production clusters.
    • If public access is required (e.g., for admins from office/VPN): define a minimal set of trusted IPv4 CIDR blocks, ideally /32 or small ranges, and ensure they do not overlap with reserved ranges.
    • Confirm your nodes and any automation can reach the private endpoint via VPC/VPN/Direct Connect/Transit Gateway.
  3. Configure private endpoint access (if not already enabled)
    If endpointPrivateAccess is false, enable it:

    aws eks update-cluster-config \
    --region us-east-1 \
    --name my-eks-cluster \
    --resources-vpc-config endpointPrivateAccess=true

    This keeps node↔API communication inside the VPC. No pod restart is required, but control‑plane connectivity paths change; ensure routing and security groups allow this.

  4. Restrict or disable public endpoint access

    • To disable public access entirely:
      aws eks update-cluster-config \
      --region us-east-1 \
      --name my-eks-cluster \
      --resources-vpc-config endpointPublicAccess=false
    • If public access is needed, tighten CIDRs to your allowlist (example uses single admin IP):
      aws eks update-cluster-config \
      --region us-east-1 \
      --name my-eks-cluster \
      --resources-vpc-config \
      "endpointPublicAccess=true,publicAccessCidrs=203.0.113.5/32,198.51.100.10/32"

    Replace the example CIDRs with your approved ranges.

  5. Verify the updated configuration
    After the update completes (may take a few minutes), re-run on 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 table

    Confirm endpointPrivateAccess is true, and that endpointPublicAccess and publicAccessCidrs match your chosen policy.

  6. Functionally test access paths

    • From a node inside the VPC, confirm API access still works:
      kubectl get nodes
    • From a client IP not in any allowed CIDR, confirm the public endpoint is no longer reachable (connection timeout or TLS failure).
    • From an allowed client IP (if public access is enabled), confirm API access succeeds with valid credentials.
Using kubectl

kubectl cannot change control plane endpoint access, because this setting is managed at the cloud provider / EKS control plane level, not via Kubernetes API objects. Use the cloud provider console/CLI/IaC to update the cluster’s endpoint access as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Report EKS control plane endpoint exposure for all clusters in an account/region.
# Requires: aws CLI, jq
#
# Run on: any machine with AWS credentials and aws/jq installed.

set -euo pipefail

AWS_REGION="${1:-us-east-1}"

echo "Inspecting EKS clusters in region: ${AWS_REGION}" >&2
echo

# Header
printf "%-40s %-8s %-7s %-18s %-30s\n" \
"CLUSTER_NAME" "PRIVATE" "PUBLIC" "PUBLIC_ACCESS_MODE" "PUBLIC_ACCESS_CIDRS"
printf -- "%.0s-" {1..120}; echo

clusters_json="$(aws eks list-clusters --region "${AWS_REGION}")"
cluster_names=($(echo "${clusters_json}" | jq -r '.clusters[]'))

if [ "${#cluster_names[@]}" -eq 0 ]; then
echo "No EKS clusters found in region ${AWS_REGION}" >&2
exit 0
fi

for cluster in "${cluster_names[@]}"; do
desc="$(aws eks describe-cluster --region "${AWS_REGION}" --name "${cluster}")"

private_access="$(echo "${desc}" \
| jq -r '.cluster.resourcesVpcConfig.endpointPrivateAccess')"
public_access="$(echo "${desc}" \
| jq -r '.cluster.resourcesVpcConfig.endpointPublicAccess')"
public_cidrs="$(echo "${desc}" \
| jq -r '.cluster.resourcesVpcConfig.publicAccessCidrs | join(",")')"

# Derived "mode" for easier review:
# - FULLY_PRIVATE: private=true, public=false
# - RESTRICTED_PUBLIC: private=true, public=true, CIDRs not 0.0.0.0/0
# - OPEN_PUBLIC: public=true and includes 0.0.0.0/0
# - PUBLIC_NO_PRIVATE: private=false, public=true (with any CIDRs)
mode="UNKNOWN"
if [ "${public_access}" = "false" ] && [ "${private_access}" = "true" ]; then
mode="FULLY_PRIVATE"
elif [ "${public_access}" = "true" ] && [[ "${public_cidrs}" == *"0.0.0.0/0"* ]]; then
mode="OPEN_PUBLIC"
elif [ "${public_access}" = "true" ] && [ "${private_access}" = "false" ]; then
mode="PUBLIC_NO_PRIVATE"
elif [ "${public_access}" = "true" ] && [ "${private_access}" = "true" ]; then
mode="RESTRICTED_PUBLIC"
fi

printf "%-40s %-8s %-7s %-18s %-30s\n" \
"${cluster}" \
"${private_access}" \
"${public_access}" \
"${mode}" \
"${public_cidrs}"
done

cat <<'EOF'

How to interpret results (potential problems):

1) endpointPrivateAccess = false
- Modes: PUBLIC_NO_PRIVATE or OPEN_PUBLIC
- Issue: Nodes and workloads must reach the API over the public internet; control-plane
traffic is not restricted to the VPC as recommended.

2) endpointPublicAccess = true AND publicAccessCidrs includes 0.0.0.0/0
- Mode: OPEN_PUBLIC
- Issue: The API server is reachable from any IP on the internet.

3) endpointPublicAccess = true AND publicAccessCidrs contains broad CIDRs
(e.g. 0.0.0.0/0, 0.0.0.0/1, 10.0.0.0/8, etc.)
- Mode: RESTRICTED_PUBLIC (but possibly overly permissive)
- Issue: Review whether each CIDR is truly required and minimize where possible.

4) Preferred states with respect to this control:
- FULLY_PRIVATE:
endpointPrivateAccess = true
endpointPublicAccess = false
- RESTRICTED_PUBLIC (if public access is required):
endpointPrivateAccess = true
endpointPublicAccess = true
publicAccessCidrs = tightly scoped, specific /32s or small ranges

Note: This script only reports configuration. Changes must be made via aws eks update-cluster-config
or your IaC/console workflow after a risk/operations review.
EOF