> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Gather current endpoint configuration (any machine with AWS CLI access)**
           ```bash theme={null}
           aws eks describe-cluster \
             --region us-east-1 \
             --name my-eks-cluster \
             --query "cluster.resourcesVpcConfig.{endpointPrivateAccess:endpointPrivateAccess,endpointPublicAccess:endpointPublicAccess,publicAccessCidrs:publicAccessCidrs}" \
             --output json
           ```
           Review:
           * `endpointPrivateAccess` should be `true`.
           * If `endpointPublicAccess` is `true`, ensure `publicAccessCidrs` is 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.

        2. **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`, and `publicAccessCidrs` set 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.

        3. **Update to private-only endpoint if public access is not required (any machine with AWS CLI access)**
           ```bash theme={null}
           aws eks update-cluster-config \
             --region us-east-1 \
             --name my-eks-cluster \
             --resources-vpc-config endpointPrivateAccess=true,endpointPublicAccess=false
           ```
           Be aware: turning off public access immediately cuts off internet-based kubectl/API access; ensure admins have connectivity via the VPC first.

        4. **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):
           ```bash theme={null}
           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).

        5. **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 `publicAccessCidrs` decided in steps 2 and 4.\
               Ensure future deployments/updates cannot revert to `0.0.0.0/0` or an empty `publicAccessCidrs`.

        6. **Verify final state matches the decision (any machine with AWS CLI access)**
           ```bash theme={null}
           aws eks describe-cluster \
             --region us-east-1 \
             --name my-eks-cluster \
             --query "cluster.resourcesVpcConfig" \
             --output json
           ```
           Confirm:
           * `endpointPrivateAccess` is `true`.
           * `endpointPublicAccess` and `publicAccessCidrs` exactly match the chosen model in step 2.
      </Accordion>

      <Accordion title="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.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/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):**

        ```bash theme={null}
        # 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` = `true` and `PUB` = `false`
          * Generally aligned with “private endpoint only”; review still recommended but low concern.

        * `RISK_INDICATOR` contains:
          * `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.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
