compliance.tf

EKS clusters endpoint should restrict public access

The EKS API server endpoint is the control plane for your Kubernetes cluster. With the public endpoint enabled, anyone who can reach it can attempt authentication. RBAC and strong authentication reduce the risk but don't eliminate it, and an exposed endpoint invites brute-force and credential-stuffing attempts.

This control takes the strict view: only a disabled public endpoint passes, with clients reaching the API server through the private VPC endpoint. Restricting public_access_cidrs to corporate or VPN ranges reduces exposure, but the cluster still fails.

Retrofit consideration

Disabling public endpoint access on a running cluster requires that private endpoint access is already enabled and that operators have network connectivity to the VPC via VPN, Direct Connect, or bastion. Cutting public access without this in place causes kubectl lockout.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "eks" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "iso27001.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "cisv80ig1.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "nist800171.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "awswellarchitected.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "nistcsfv11.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}
module "eks" {
  source  = "pcidssv321.compliance.tf/terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"
}

This control is enforced automatically with Compliance.tf modules. Start free trial

If you use terraform-aws-modules/eks/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = ">=21.0.0"

  include_oidc_root_ca_thumbprint = false
  name = "abc123"
  subnet_ids = ["subnet-abc123", "subnet-def456"]
  vpc_id = "${data."vpc-12345678"}"

  endpoint_public_access = false
}

Use AWS provider resources directly. See docs for the resources involved: aws_eks_cluster.

resource "aws_eks_cluster" "this" {
  enabled_cluster_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]

  encryption_config {
    provider {
      key_arn = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
    }
    resources = ["secrets"]
  }

  name     = "example-abc123"
  role_arn = "arn:aws:iam::123456789012:role/example-role"

  vpc_config {
    endpoint_private_access = true
    endpoint_public_access  = false
    subnet_ids              = ["subnet-abc123", "subnet-def456"]
  }
}

What this control checks

The aws_eks_cluster resource's vpc_config block controls API endpoint exposure. To pass, set endpoint_public_access = false. Also set endpoint_private_access = true so worker nodes and internal clients reach the API server over the private VPC endpoint. Any configuration with endpoint_public_access = true fails, including one where public_access_cidrs limits access to specific ranges.

Common pitfalls

Default endpoint_public_access is true

Omit endpoint_public_access and it defaults to true, which fails this control. Set endpoint_public_access = false explicitly on every cluster.

Disabling public without enabling private causes lockout

Set endpoint_public_access = false without endpoint_private_access = true and the API server goes dark. There's no graceful error, kubectl just stops responding. Enable the private endpoint first, verify connectivity from within the VPC, then disable public access.

Restricting CIDRs does not pass

Setting public_access_cidrs to a VPN or office range is a real improvement, but the control fails any cluster with endpoint_public_access = true. If you need public access, document the exception and keep the CIDR list tight.

DNS resolution changes with private endpoint

When endpoint_private_access is enabled, the EKS API DNS name resolves to private IPs from within the VPC. Developers and CI/CD runners outside the VPC only reach the API server if endpoint_public_access is still on. Sort out DNS and network routing before toggling these flags, otherwise you'll break pipelines silently.

Audit evidence

Auditors typically want AWS Config rule results showing each EKS cluster as compliant, or equivalent CSPM findings. Console evidence from the EKS cluster's Networking tab, showing public access as disabled or restricted to specific CIDRs, is a common request. aws eks describe-cluster --name <name> output with endpointPublicAccess: false or a non-wildcard publicAccessCidrs list is the CLI-level proof.

CloudTrail logs for UpdateClusterConfig events show when and by whom the endpoint configuration was last changed, useful for change-control review.

Framework-specific interpretation

PCI DSS v4.0: Requirement 1 mandates network controls that prevent unauthorized access to systems in the cardholder data environment. An open EKS API endpoint, reachable from any IP, directly contradicts this: the Kubernetes control plane governs every workload on the cluster, so it needs the same network boundaries as any other CDE component. Disabling public access or scoping public_access_cidrs to known IP ranges provides the segmentation PCI assessors look for.

ISO/IEC 27001:2022: A.8.20 (Network Security) and A.8.22 (Segregation of Networks) both apply. The EKS API endpoint is a management interface with broad lateral movement potential, and locking it to private VPC access or a narrow CIDR set is the kind of explicit network boundary these controls call for. A.8.9 (Configuration Management) adds a secondary angle: accepting the insecure default of open public access is itself a configuration management finding.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: eks_cluster_endpoint_restrict_public_access
  • AWS Config Managed Rule: EKS_ENDPOINT_NO_PUBLIC_ACCESS
  • Checkov Checks: CKV_AWS_38, CKV_AWS_39
  • Powerpipe Controls: aws_compliance.control.eks_cluster_endpoint_public_access_restricted, aws_compliance.control.eks_cluster_endpoint_restrict_public_access
  • AWS Security Hub Control: EKS.1
  • Trivy Check: AWS-0040

Last reviewed: 2026-10-03

On this page

Ask AI about this

Help improve this page