Securing GKE Private Clusters: Network and Egress Design

9 min read
GKEGCPNetworkingCloud NATKubernetesSecurity

For most platform teams the moment a GKE cluster stops being a prototype is also the moment its network topology starts costing them. A cluster that was created quickly, with default node zones, public control-plane access, and unplanned pod ranges, is hard to harden later. Retro-fitting a private cluster, truncating IP space, or inserting a NAT gateway after workloads are live is disruptive, and it often forces downtime or re-creation. The decisions that matter for a secure, stable GKE footprint are largely made at design time: how the control plane is reachable, how pod and service ranges are carved, how egress leaves the cluster, and where traffic enters it. This guide walks through those decisions in the order a platform lead should make them, and pairs each one with a concrete, verifiable configuration for engineering leaders and DevOps leads.

Start private: keep the control plane off the public internet

A public cluster exposes the Kubernetes API server to the internet, protected only by Cloud IAM and whatever authorized-networks range you set. The Google Cloud recommendation is to run GKE with a private cluster, so nodes get internal IP addresses only and the control plane is accessed over the same VPC that your services already trust. A private cluster still lets the kubelet reach the API server through a private endpoint, and it keeps the master from needing a public address at all when you set the endpoint to private-only.

When you create the cluster, the control plane is put on a Google-managed network with an RFC 1918 range by default, and Google Cloud provisions the required firewall and routes so nodes reach it over the private endpoint. For most teams the extra isolation is worth the small setup cost, particularly when combined with a well-scoped set of control-plane access ranges. The key point is that private is a creation-time flag: you cannot flip an existing public cluster to private without recreating it.

VPC-native routing and secondary ranges

Modern GKE clusters are VPC-native. Pods and Services live in alias IP ranges carved from your VPC, so traffic between pods is routed by the VPC router rather than by a kube-proxy overlay. This gives you direct peerings to on-premises networks, predictable IP accounting, and simpler troubleshooting. The trade-off is that you must plan the ranges up front, because the pod and service ranges are fixed when the cluster is created.

Choose a node subnet with a generous CIDR and reserve two secondary ranges for pods and services. A common mistake is giving the pod secondary range only /24, then hitting IP exhaustion on node scaling. A reasonable starting plan is a /16 pod range and /20 service range in a large org, leaving room to grow. When nodes scale, the cluster can allocate secondary pod CIDRs within those ranges but cannot reclaim or expand them once the cluster exists, so err toward headroom.

Control which subnets the cluster can use

In a shared-VPC or hub-and-spoke design, keep your cluster node subnets to the segments you control and peer them explicitly. Do not let the cluster default to a broad subnet that overlaps with databases, VPN tunnels, or your on-premises CIDRs. Address overlap is the most common cause of mystifying connectivity failures in hybrid deployments, and it is very hard to fix after the cluster is serving traffic.

Master Authorized Networks and private endpoint access

Even on a private cluster, you should restrict the control plane further with master authorized networks. This setting limits the IP ranges that may reach the Kubernetes control plane, and it is enforceable alongside your private endpoint. When workloads and administrators are on the same VPC, you can set the private endpoint to be the only access path and allow only relevant source CIDRs, such as jump hosts or a Cloud VPN or Interconnect range, to reach it.

A minimal restricted setup is a private cluster with an authorized networks range of 10.0.0.0/8 for the VPC and a single admin jump host /32, with no public endpoint configured. Thinking of the control plane as a server like any other, with explicit bring-your-own access, keeps the blast radius small should a CI token or kubeconfig leak.

Controlled egress with Cloud NAT

Nodes in a private cluster have no public IPs, which means outbound internet access must be provided by a NAT gateway. Cloud NAT is the managed option: it lets instances without external IPs send outbound traffic while tracking connection state and mapping them to static addresses you control. You attach it at the region and subnet level, so it is a single coherent egress path for the whole cluster.

For many clusters a single Cloud NAT per region is enough, but do not assume stateless NAT is safe by default. Route all egress through NAT endpoints you own, set static IPs so allowlists on third-party APIs can be scoped to known ranges, and treat the NAT as a choke point where you log and inspect outbound traffic. A simple routing fallback with two NAT gateways in two zones gives you redundancy if one endpoint fails.

Layered ingress and workload isolation

Ingress should be a deliberate, layered path rather than a default allow. Put a managed load balancer or gateway in front of services, terminate TLS at the edge, and keep control-plane ports out of the ingress path. Inside the cluster, enforce zero-trust-style network policies so that a compromised pod cannot freely reach every other service by default. Network policies are the practical enforcement point for that east-west isolation, and they pair with the private, VPC-native foundation to give you defense in depth.

Enforce defaults with a deny-by-default policy

A deny-by-default NetworkPolicy denies all ingress and egress to any pod not covered by an allow policy. When a new workload is added, its team must explicitly declare what it is allowed to talk to, which forces the access review that a default-allow cluster never gets. Audit every few months: an allow rule with no backing Service is dead policy and should be removed.

A practical hardening checklist

Walk the following list before you promote a GKE cluster to production, and re-run it after any network or scaling change.

  • The cluster is private and the API server has no public endpoint configured.
  • Pod and service secondary ranges are large enough for three years of node scaling, and checked for overlap with every peered CIDR.
  • Master authorized networks lists only the control-plane client ranges (VPC, jump hosts, CI runners) and no public 0.0.0.0/0.
  • All node subnets are egressed through Cloud NAT with static addresses; no node has a public IP by default.
  • Ingress terminates TLS at the load balancer and exposes only the ports the service is meant to serve.
  • A deny-by-default NetworkPolicy is present, and every allow rule maps to a live Service.
  • Outbound traffic to third-party APIs flows through known NAT addresses and is logged for review.

Checking the private setting is a one-liner that confirms the control plane is on the private path only.

gcloud container clusters describe CLUSTER \\ --region=REGION --format="table(privateClusterConfig.enablePrivateEndpoint,privateClusterConfig.masterIpv4CidrBlock)"

Confirm enablePrivateEndpoint is true and the master range is the narrow private block you intended, not a default range you meant to tighten.

If your team has a cluster that grew organically and you are not sure whether its control plane, egress, and policy model are production-safe, Secpros can review your GKE network design and return a short prioritized audit plan: which subnets and ranges to re-plan, what to gate before the next migration, and which NetworkPolicies to default-on first.

/ author

Pawel Bedynski

DevOps Engineer & Kubernetes Consultant. Building cloud-native infrastructure on GCP since 2019. 80+ production clusters deployed.

LinkedIn