GKE Private Cluster NAT and Network Policy
Scenario
- Create a GKE private cluster and use a bastion to connect to the control plane via internal private IP
- All hosts connect externally through NAT
- Use GKE network policy for connection control
Process and Description
- Create GKE Private Cluster and bastion host
- Configure the bastion host, install necessary packages
- Create CloudNAT to provide external network connections
- Test network policy connection management and NAT external functionality
Execution Steps
Create GKE Private Cluster and Bastion Host
1export PROJECT_ID="YOUR_PROJECT_ID"2export NETWORK_NAME="YOUR_NETWORK_NAME"3export SUBNET_NAME="YOUR_SUBNET_NAME"4export REGION="YOUR_REGION"5export ZONE="YOUR_ZONE"6export GKE_CLUSTER_NAME="YOUR_GKE_CLUSTER_NAME"7export BASTION_VM_NAME="YOUR_BASTION_VM_NAME"8export ROUTER_NAME="YOUR_ROUTER_NAME"9
10# create network11gcloud compute networks create ${NETWORK_NAME} --subnet-mode custom12gcloud compute networks subnets create ${SUBNET_NAME} \13 --network ${NETWORK_NAME} \14 --region ${REGION} \15 --range 192.168.1.0/2416gcloud compute firewall-rules create allow-ssh \17 --network ${NETWORK_NAME} \18 --source-ranges 35.235.240.0/20 \19 --allow tcp:2220
21# create private cluster22gcloud container clusters create ${GKE_CLUSTER_NAME} \23 --zone ${ZONE} \24 --cluster-version "latest" \25 --machine-type "n2-standard-2" \26 --disk-type "pd-standard" \27 --disk-size "100" \28 --scopes "https://www.googleapis.com/auth/compute","https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" \29 --num-nodes "1" \30 --enable-private-nodes \31 --enable-private-endpoint \32 --master-ipv4-cidr "172.16.0.0/28" \33 --enable-ip-alias \34 --network "projects/${PROJECT_ID}/global/networks/${NETWORK_NAME}" \35 --subnetwork "projects/${PROJECT_ID}/regions/${REGION}/subnetworks/${SUBNET_NAME}" \36 --max-nodes-per-pool "110" \37 --enable-master-authorized-networks \38 --addons HorizontalPodAutoscaling,HttpLoadBalancing \39 --enable-autoupgrade \40 --enable-autorepair \41 --enable-network-policy42
43# create bastion44gcloud compute instances create ${BASTION_VM_NAME} \45 --project=${PROJECT_ID} \46 --zone=${ZONE} \47 --machine-type=e2-micro \48 --network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=${SUBNET_NAME} \49 --maintenance-policy=MIGRATE \50 --provisioning-model=STANDARD \51 --create-disk=auto-delete=yes,boot=yes,device-name=ricky-bastion,image=projects/ubuntu-os-cloud/global/images/ubuntu-2204-jammy-v20240801,mode=rw,size=10,type=projects/${PROJECT_ID}/zones/${ZONE}/diskTypes/pd-balanced \52 --no-shielded-secure-boot \53 --shielded-vtpm \54 --shielded-integrity-monitoring \55 --labels=goog-ec-src=vm_add-gcloud \56 --reservation-affinity=any57export BASTION_IP=$(gcloud compute instances describe ${BASTION_VM_NAME} --zone ${ZONE} --format='get(networkInterfaces[0].networkIP)')58
59# add bastion private to GKE authorized networks60gcloud container clusters update ${GKE_CLUSTER_NAME} --zone=${ZONE} \61 --enable-master-authorized-networks \62 --master-authorized-networks ${BASTION_IP}/32Configure Bastion Host
Install necessary packages, this will take about 5 minutes
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "sudo snap remove google-cloud-cli && \2sudo apt-get update && \3sudo apt-get install apt-transport-https ca-certificates gnupg curl -y && \4curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg && \5echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \6sudo apt-get update && sudo apt-get install google-cloud-cli kubectl google-cloud-cli-gke-gcloud-auth-plugin -y"Log in to the gcloud account on the bastion host
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "gcloud auth login"Create test pods, add a label to the test pod for later restriction of external connections
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "gcloud container clusters get-credentials ${GKE_CLUSTER_NAME} --zone=${ZONE} && \2kubectl get no && \3kubectl run test --image nginx -l net=limited && \4kubectl run test2 --image nginx && \5kubectl get po"Enter the pod to test and confirm if external connections are possible
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "kubectl exec -it test -- apt update"Create CloudNAT
1gcloud compute routers create ${ROUTER_NAME} \2 --network ${NETWORK_NAME} \3 --region ${REGION}4gcloud compute routers nats create nat-config \5 --router-region ${REGION} \6 --router ${ROUTER_NAME} \7 --nat-all-subnet-ip-ranges \8 --auto-allocate-nat-external-ips(Optional) Enable GKE Network Policy Feature
If it’s an existing GKE cluster, you can enter the following command to enable it
1gcloud container clusters update ${GKE_CLUSTER_NAME} --zone=${ZONE} --update-addons=NetworkPolicy=ENABLED2gcloud container clusters update ${GKE_CLUSTER_NAME} --zone=${ZONE} --enable-network-policyTesting
External and Internal Connection Testing
Enter the pod to test and confirm if external connections are possible
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "kubectl exec -it test -- apt update"1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- 'TEST2_IP=$(kubectl get po test2 -o jsonpath='{.status.podIP}') && \2kubectl exec -it test -- curl ${TEST2_IP} && \3kubectl exec -it test -- curl https://google.com'Expected result
29 collapsed lines
1<!DOCTYPE html>2<html>3<head>4<title>Welcome to nginx!</title>5<style>6html { color-scheme: light dark; }7body { width: 35em; margin: 0 auto;8font-family: Tahoma, Verdana, Arial, sans-serif; }9</style>10</head>11<body>12<h1>Welcome to nginx!</h1>13<p>If you see this page, the nginx web server is successfully installed and14working. Further configuration is required.</p>15
16<p>For online documentation and support please refer to17<a href="http://nginx.org/">nginx.org</a>.<br/>18Commercial support is available at19<a href="http://nginx.com/">nginx.com</a>.</p>20
21<p><em>Thank you for using nginx.</em></p>22</body>23</html>24<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">25<TITLE>301 Moved</TITLE></HEAD><BODY>26<H1>301 Moved</H1>27The document has moved28<A HREF="https://www.google.com/">here</A>.29</BODY></HTML>Create Network Policy to Restrict Test
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "echo 'apiVersion: networking.k8s.io/v12kind: NetworkPolicy3metadata:4 name: default-deny-egress5 namespace: default6spec:7 podSelector:8 matchLabels:9 net: limited10 policyTypes:11 - Egress12 egress:13 - to:14 - ipBlock:15 cidr: 10.0.0.0/816 - ipBlock:17 cidr: 172.16.0.0/1218 - ipBlock:19 cidr: 192.168.0.0/16' > deny-all.yaml && \20kubectl apply -f deny-all.yaml"Test connection from test pod to test2 pod, should be successful
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- 'TEST2_IP=$(kubectl get po test2 -o jsonpath='{.status.podIP}') && \2kubectl exec -it test -- curl ${TEST2_IP}'Test external connection from test pod, should fail
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "kubectl exec -it test -- curl https://google.com"Test external connection from test2 pod, should succeed
1gcloud compute ssh ${BASTION_VM_NAME} --zone ${ZONE} --tunnel-through-iap -- "kubectl exec -it test2 -- curl https://google.com"Clean Up
1gcloud compute instances delete ${BASTION_VM_NAME} --zone=${ZONE} --quiet2gcloud container clusters delete ${GKE_CLUSTER_NAME} --zone=${ZONE} --quiet3gcloud compute routers delete ${ROUTER_NAME} --region ${REGION} --quiet4gcloud compute firewall-rules delete allow-ssh --quiet5gcloud compute networks subnets delete ${SUBNET_NAME} --region ${REGION} --quiet6gcloud compute networks delete ${NETWORK_NAME} --quiet