This check flags EKS clusters whose Kubernetes API server endpoint is reachable from the public internet. A public endpoint widens your attack surface to brute-force, credential abuse, and zero-day exploitation. Restrict access by enabling the private endpoint and locking public access down to known CIDRs (or disabling it entirely).
The Kubernetes API server is the front door to your cluster. Every kubectl command, every controller, every CI/CD deploy, and every operator talks to it. When EKS provisions a cluster, it can expose that API server endpoint publicly, privately within your VPC, or both. This Lensix check fires when the public endpoint is enabled and open to 0.0.0.0/0, meaning anyone on the internet can reach it.
That does not automatically mean your cluster is compromised. EKS still requires valid IAM authentication and RBAC authorization to do anything useful. But a publicly reachable API server is an exposed authentication surface, and exposed surfaces get probed.
What this check detects
The check inspects the resourcesVpcConfig of each EKS cluster and looks at two fields:
- endpointPublicAccess — whether the API server is reachable from outside the VPC.
- publicAccessCidrs — the list of CIDR blocks allowed to reach the public endpoint.
It flags a cluster as failing when endpointPublicAccess is true and publicAccessCidrs contains 0.0.0.0/0. In that configuration, the API endpoint resolves to a public IP and accepts connections from any source address on the internet.
Note: EKS exposes the API server through a managed endpoint. There are three valid modes: public only (the default), public and private, and private only. The "private" endpoint routes traffic over ENIs inside your VPC, so nodes and in-VPC clients reach the API server without leaving the network.
Why it matters
IAM authentication and RBAC are real controls, and they do most of the heavy lifting. The problem is what happens before authorization ever runs. A public endpoint gives attackers something to interact with, and that creates a chain of risks.
Credential exposure becomes immediately exploitable
EKS authenticates API requests using AWS IAM via the aws-iam-authenticator flow. If an IAM access key with cluster access leaks (committed to a repo, pasted into a log, lifted from a compromised laptop), an attacker with that key can hit a public endpoint from anywhere. With a private-only endpoint, the same leaked key is useless unless the attacker is already inside your VPC or has VPN access.
Reconnaissance and probing
Public endpoints are continuously scanned. Attackers fingerprint the Kubernetes version from the /version endpoint and other responses, then match it against known CVEs. The API server has had its share of authentication and authorization bugs over the years, and a control plane you cannot reach is a control plane you cannot exploit.
Anonymous and misconfigured RBAC
If anyone has ever bound overly broad permissions to system:anonymous or system:unauthenticated (it happens more often than people admit), a public endpoint turns that mistake into a direct path to cluster takeover. RBAC misconfigurations are far more dangerous when the door is open to the world.
Warning: A public endpoint with no CIDR restriction is a common finding in clusters created via quick-start guides or default eksctl commands. Convenience during a workshop often turns into a long-lived production exposure that nobody revisits.
How to fix it
You have three sensible target states, listed from most to least restrictive:
- Private only — best for clusters whose clients all live inside the VPC, a peered VPC, or reach in over VPN/Direct Connect.
- Public and private, with CIDR restrictions — public access narrowed to known office, VPN, or CI/CD egress IPs.
- Public with restricted CIDRs — least preferred, but still far better than
0.0.0.0/0.
Option A: Restrict the public CIDR list (least disruptive)
If you need public access but want to close the wide-open door, scope it to specific source ranges.
aws eks update-cluster-config \
--name my-cluster \
--resources-vpc-config endpointPublicAccess=true,publicAccessCidrs="203.0.113.10/32,198.51.100.0/24",endpointPrivateAccess=true
This keeps the public endpoint reachable only from the listed CIDRs and enables the private endpoint so in-VPC traffic stays internal.
Warning: Before restricting CIDRs, confirm the egress IPs of every legitimate client: CI/CD runners, GitOps controllers running outside the cluster, admin laptops behind a corporate VPN, and any third-party tooling. Forgetting one will lock out a pipeline or an on-call engineer at the worst possible time.
Option B: Go private-only
If all your API clients are inside the VPC (or reach it through peering, VPN, or Direct Connect), disable public access entirely.
Danger: Switching to private-only cuts off any client that currently reaches the API server over the public internet, including external CI/CD pipelines and remote admins not on the VPN. Validate your private connectivity path before running this, or you will lock yourself out of the cluster.
aws eks update-cluster-config \
--name my-cluster \
--resources-vpc-config endpointPublicAccess=false,endpointPrivateAccess=true
The update is asynchronous. Track it until it completes:
aws eks describe-update \
--name my-cluster \
--update-id <update-id-from-previous-command>
Then verify the resulting configuration:
aws eks describe-cluster \
--name my-cluster \
--query 'cluster.resourcesVpcConfig.{public:endpointPublicAccess,private:endpointPrivateAccess,cidrs:publicAccessCidrs}'
Fixing it in Terraform
If you manage EKS with Terraform, make the change in code so it does not drift back. Using the terraform-aws-modules/eks module:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "my-cluster"
cluster_version = "1.30"
cluster_endpoint_public_access = false
cluster_endpoint_private_access = true
cluster_endpoint_public_access_cidrs = []
# ... vpc, subnets, node groups, etc.
}
If you must keep public access, scope it explicitly:
cluster_endpoint_public_access = true
cluster_endpoint_private_access = true
cluster_endpoint_public_access_cidrs = ["203.0.113.10/32", "198.51.100.0/24"]
Accessing a private cluster
Once the endpoint is private, you reach it from inside the network. Common patterns:
- A bastion host or jump box in the VPC with
kubectlinstalled. - AWS Systems Manager Session Manager into an instance in the VPC (no inbound SSH needed).
- Client VPN or Direct Connect terminating into the VPC, with DNS resolution to the private endpoint.
- CI/CD runners deployed inside the VPC or a peered network.
Note: Private endpoint resolution depends on VPC DNS. The endpoint hostname resolves to private IPs only for clients that use the VPC's Route 53 resolver. If you reach the cluster over peering or VPN, make sure DNS resolution and the inbound resolver path are configured, otherwise the hostname will still resolve to the public IP.
How to prevent it from happening again
Fixing one cluster is easy. Keeping every future cluster compliant is the real work. Push the control left so a public endpoint never reaches production.
Block it in CI with policy-as-code
Use Checkov or a similar scanner on your Terraform plans. Checkov ships a built-in rule, CKV_AWS_39, that fails when the EKS public endpoint is enabled.
checkov -d ./infra --check CKV_AWS_39,CKV_AWS_38
CKV_AWS_38 additionally flags public access open to 0.0.0.0/0, so run both. Wire this into your pipeline as a gate that blocks the merge on failure.
Enforce with OPA / Conftest
For teams running Open Policy Agent against Terraform plans:
package eks
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_eks_cluster"
resource.change.after.vpc_config[_].endpoint_public_access == true
cidrs := resource.change.after.vpc_config[_].public_access_cidrs
cidrs[_] == "0.0.0.0/0"
msg := sprintf("EKS cluster '%s' exposes its public API endpoint to 0.0.0.0/0", [resource.address])
}
Detect drift at runtime
IaC gates only catch what goes through IaC. Someone with console access can flip the setting by hand. Continuous monitoring closes that gap. Lensix runs the eks_publicendpoint check on a schedule across your accounts and alerts when a cluster drifts into a public, unrestricted state, so a manual change does not sit unnoticed for months.
Tip: Pair the IaC gate with a Service Control Policy. While SCPs cannot directly read the EKS endpoint config, you can deny eks:UpdateClusterConfig and eks:CreateCluster to all but a small set of pipeline roles. That forces every endpoint change through reviewed automation instead of the console.
Best practices
- Default to private. Treat a public endpoint as an exception that requires justification, not the starting point. Most workloads do not need the world to reach their control plane.
- If public access is required, always scope CIDRs. Never leave
publicAccessCidrsat0.0.0.0/0. Tie the allowed ranges to your VPN, NAT, or CI/CD egress IPs and document them. - Audit RBAC regularly. A restricted endpoint reduces blast radius, but it does not excuse over-permissive roles. Look for bindings to
system:anonymous,system:unauthenticated, and any wildcard cluster-admin grants. - Enable control plane logging. Turn on the
auditandauthenticatorlog types so you can see who is calling the API and from where.
aws eks update-cluster-config \
--name my-cluster \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator"],"enabled":true}]}'
A private API endpoint will not stop a determined attacker who already has a foothold in your VPC, and it is not a substitute for tight RBAC and credential hygiene. But it removes an entire class of internet-facing risk for the cost of a single config change. For most teams, that is one of the highest-value EKS hardening steps available.

