The dangerous version of this change is one command that flips the endpoint and locks every administrator out at the same time. The safe version keeps both paths open, proves the private one works from the VPN, and only then retires the public endpoint. Assume an existing cluster, an approved tunnel into its VPC and a kubeconfig that already works; the job is private access to the Kubernetes API, not private nodes and not a new permission model. Every step below has a check you can fail safely.

1. Inventory the endpoint and keep a way back in

Record the current state before changing it, including the public CIDR allowlist and the cluster security group. Those two are what you will compare against when something stops working, and the allowlist is also your rollback path if the private route turns out to be wrong.

aws eks describe-cluster --region REGION --name CLUSTER \
  --query 'cluster.{endpoint:endpoint,public:resourcesVpcConfig.endpointPublicAccess,private:resourcesVpcConfig.endpointPrivateAccess,cidrs:resourcesVpcConfig.publicAccessCidrs}'

REGION and CLUSTER are placeholders here. Note what the private flag reads now, because a cluster created with private access already enabled needs a different first step than one that has never had it. Keep at least one approved administration path open during the transition; a VPN that depends on the same cluster being reachable is not a recovery path.

2. Enable private access with the network path in place

Turning the flag on is the easy half. The tunnel needs a route to the endpoint's private addresses, and the cluster security group needs TCP 443 from the source the VPN actually presents. A Client VPN association, a site-to-site gateway and a peered VPC can all present different source addresses, so use the model you deployed instead of a generic private range.

aws eks update-cluster-config --region REGION --name CLUSTER \
  --resources-vpc-config endpointPublicAccess=true,endpointPrivateAccess=true

aws eks describe-update --region REGION --name CLUSTER --update-id UPDATE_ID

The update is asynchronous. Poll the update id until it reports success rather than waiting a fixed number of seconds and assuming. Leave node and control-plane traffic alone while you do this: replacing the cluster's whole security group with a rule for your laptop is how a working data plane becomes a second incident.

The EKS endpoint documentation is worth reading once in full, because it says private-only access can come from the VPC or a connected network. That sentence is the whole reason this migration is possible, and it is also why the next step exists.

3. Prove the workstation used the private path

With both endpoints enabled, a successful kubectl get ns proves nothing about which one answered. Resolve the endpoint hostname and check whether the address you got belongs to the private range or the public one. If your connected network does not receive the private view through DNS, the command will succeed over the public endpoint and you will retire the public path on false evidence. That is the single most common way this migration goes wrong, and it looks exactly like success.

nslookup HASH.gr7.REGION.eks.amazonaws.com
kubectl cluster-info
kubectl get ns --request-timeout=10s

Keep the hostname and certificate validation exactly as they are. Substituting a private IP in the kubeconfig and skipping TLS verification is not the migration you described in the change record, and it will fail again the moment the endpoint rotates.

4. Retire public access deliberately

Once the private path is demonstrated and the recovery workflow is approved, close the public endpoint. This is a real change rather than a preview, so run it against the account and settings you actually mean.

aws eks update-cluster-config --region REGION --name CLUSTER \
  --resources-vpc-config endpointPublicAccess=false,endpointPrivateAccess=true

In private-only mode the public DNS name resolves to the endpoint's private addresses from inside the VPC, so resolution and reachability stop being the same thing. Anything that relied on the public address, including CI runners or a laptop on a different network, now needs its own path in.

5. Keep network policy and identity policy separate

The public access CIDR list governs the public endpoint only. It does not constrain the private endpoint, which is bounded by the cluster security group and the connected-network policy instead. If you keep the public endpoint, its allowlist must match the public egress your API clients and nodes actually use; a VPN pool address is rarely that source.

None of this changes IAM or Kubernetes RBAC. Endpoint settings are an administrative action, and ordinary users should not receive permission to relax the boundary just to reach the API. Write the final state, the date, and the recovery path into the runbook, then test the recovery path once while it is still easy.

Pennadoù liammet