1. Panoramica
Questo lab ti introduce a ClusterNetworkPolicy su Google Kubernetes Engine (GKE). ClusterNetworkPolicy applicato per vari casi d'uso e può essere applicato a livello di cluster e applicato ai livelli Amministratore e Baseline. In questo esempio mostreremo il controllo con un deployment LLM (Gemma) su GKE con TPU, vLLM e convalida.
Lavorerai con le seguenti tecnologie:
- Google Kubernetes Engine (GKE): la piattaforma di orchestrazione dei container di base per l'hosting di workload privati e scalabili.
- ClusterNetworkPolicy (CNP): policy di rete multilivello con ambito cluster che abilitano i guardrail SecOps nei livelli Admin, NetworkPolicy e Baseline.
- Motore di inferenza vLLM TPU: inferenza ad alta velocità di trasmissione per il modello leggero Gemma 4 (google/gemma-4-E2B-it) su TPU Google con endpoint REST compatibili con OpenAI.
Ruoli e personas
L'API policy di rete è strutturata in base a specifici ruoli dell'organizzazione:
- Utilizzo da parte dell'amministratore del cluster (
ClusterNetworkPolicy): applica i guardrail di sicurezza ai cluster nei livelli Admin e Baseline. - Utilizzo da parte dell'amministratore dello spazio dei nomi (
NetworkPolicy): gestisce i confini e l'isolamento a livello di tenant all'interno di spazi dei nomi specifici. - Utilizzo da parte degli sviluppatori di applicazioni (
NetworkPolicy): definisce regole granulari per i microservizi specifici dell'applicazione.
Per configurare questo ambiente, eseguirai il deployment di un VPC privato personalizzato, di Cloud NAT e di un cluster GKE privato con un pool di nodi TPU dedicato.
Per proteggere il cluster, creerai risorse ClusterNetworkPolicy multilivello insieme a oggetti NetworkPolicy standard per applicare rigide misure di sicurezza di rete. Quindi,eseguirai il deployment di un workload di AI che utilizza vLLM e le Cloud TPU di Google Cloud per fornire il modello Gemma 4.
Infine, stabilirai una base di riferimento funzionante, applicherai le policy di sicurezza e configurerai NetworkLogging utilizzando Cloud Logging e flussi di osservabilità eBPF di Hubble in tempo reale per controllare le decisioni relative alle norme e verificare i pacchetti ignorati. Tutte le configurazioni utilizzeranno una combinazione di Terraform, gcloud e kubectl.
In questo lab imparerai a:
- Esegui il provisioning del networking privato, di un cluster GKE e degli acceleratori Cloud TPU v5e utilizzando Terraform.
- Configura GKE NetworkLogging per registrare la telemetria della connessione.
- Esegui il deployment di Gemma 4 E2B su TPU utilizzando l'immagine container vllm/vllm-tpu ufficiale.
- Stabilisci una base di riferimento funzionante che dimostri la connettività end-to-end prima di applicare le limitazioni.
- Applica una risorsa ClusterNetworkPolicy
Baseline-tierper negare per impostazione predefinita il traffico non autorizzato tra spazi dei nomi. - Applica un criterio NetworkPolicy gestito dallo sviluppatore per delegare l'accesso ai client autorizzati.
- Applica un oggetto ClusterNetworkPolicy
Admin-tierper applicare misure di sicurezza globali per il traffico in uscita che sostituiscono le impostazioni degli sviluppatori. - Verifica i pacchetti persi e passati in tempo reale utilizzando (stream eBPF Hubble e Cloud Logging).
In questo lab creerai il seguente pattern.
Figura 1. 
2. Configurazione dei servizi Google Cloud
Configurazione dell'ambiente autonomo
- Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.



- Il Nome progetto è il nome visualizzato per i partecipanti di questo progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarla.
- L'ID progetto è univoco per tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Cloud genera automaticamente una stringa univoca; di solito non importa quale sia. Nella maggior parte dei codelab, dovrai fare riferimento al tuo ID progetto (in genere identificato come
PROJECT_ID). Se non ti piace l'ID generato, puoi generarne un altro casuale. In alternativa, puoi provare a inserire un nome di tua scelta e verificare se è disponibile. Non può essere modificato dopo questo passaggio e rimane per tutta la durata del progetto. - Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
- Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare le risorse/API Cloud. L'esecuzione di questo codelab non costerà molto, se non addirittura nulla. Per arrestare le risorse ed evitare che ti vengano addebitati costi oltre a quelli di questo tutorial, puoi eliminare le risorse che hai creato o eliminare il progetto. I nuovi utenti Google Cloud possono beneficiare del programma di prova senza costi di 300$.
Avvia Cloud Shell
Sebbene Google Cloud possa essere utilizzato da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.
Nella console Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

Bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere un risultato simile al seguente:

In questa macchina virtuale sono caricati tutti gli strumenti per sviluppatori di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Tutto il lavoro in questo codelab può essere svolto all'interno di un browser. Non è necessario installare nulla.
3. Configura l'ambiente con Terraform
Per completare questo lab,
- Devi avere accesso alla quota di TPU v5e nella regione e nella zona selezionate, ad esempio (
us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b). Per ottenere l'accesso, devi seguire il documento del piano TPU e abilitare la quota di TPU. - Si tratta di un deployment di piccole dimensioni che richiede 4 chip TPU v5e (
ct5lp-hightpu-4t),che formeranno una sezione 2x2 in una singola zona.
Per utilizzare Hugging Face
- Token Hugging Face: è necessario un token di accesso per scaricare i pesi del modello Gemma.
- Accetta eventuali contratti per l'utilizzo, se richiesto.
Creeremo un VPC personalizzato con regole firewall, una subnet e un cluster Autopilot. Apri la console Cloud e seleziona il progetto che utilizzerai.
- Apri Cloud Shell nella parte in alto a destra della console, assicurati di visualizzare l'ID progetto corretto in Cloud Shell e conferma eventuali prompt per consentire l'accesso.

- Crea una directory denominata
gke-cnp-lab,e spostati al suo interno.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- Ora crea alcune variabili. P.S. Modifica REGION e ZONE in modo che riflettano la posizione effettiva in cui hai accesso alle risorse TPU. Il valore predefinito utilizzato nella regione
us-west4e nella zonaus-west4-adel lab
export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-west4"
export ZONE="us-west4-a"
export NETWORK="cnp-demo-vpc"
export SUBNET="cnp-main-subnet"
export CLUSTER_NAME="cnp-demo-cluster"
- Salva le variabili di configurazione in uno script helper nella directory gke-cnp-lab. Se la sessione di Cloud Shell si disconnette o si reimposta, puoi ripristinare rapidamente le variabili attive eseguendo
source ./env.sh
cat << EOF > env.sh
export PROJECT_ID="${PROJECT_ID}"
export REGION="${REGION}"
export ZONE="${ZONE}"
export NETWORK="${NETWORK}"
export SUBNET="${SUBNET}"
export CLUSTER_NAME="${CLUSTER_NAME}"
echo -e "\n=== Validating Environment Configuration ==="
echo "Project ID : \${PROJECT_ID}"
echo "Region : \${REGION}"
echo "Zone : \${ZONE}"
echo "Network Name : \${NETWORK}"
echo "Subnet Name : \${SUBNET}"
echo "Cluster Name : \${CLUSTER_NAME}"
if [[ -z "\${PROJECT_ID}" || -z "\${REGION}" || -z "\${ZONE}" || -z "\${NETWORK}" || -z "\${SUBNET}" || -z "\${CLUSTER_NAME}" ]]; then
echo -e "\n[ERROR] One or more environment variables are missing! Check your configuration."
else
echo -e "\n[SUCCESS] Environment variables successfully populated and validated."
fi
EOF
chmod +x env.sh
source ./env.sh
- Ora aggiungi alcuni file di configurazione. In questo modo verranno creati i seguenti file terraform.tfvars , variables.tf, main.tf nella directory gke-cnp-lab.
cat << EOF > terraform.tfvars
project_id = "${PROJECT_ID}"
region = "${REGION}"
zone = "${ZONE}"
network_name = "${NETWORK}"
subnet_name = "${SUBNET}"
cluster_name = "${CLUSTER_NAME}"
EOF
cat << 'EOF' > variables.tf
variable "project_id" {
type = string
description = "Google Cloud Project ID"
}
variable "region" {
type = string
description = "Target GCP region"
}
variable "zone" {
type = string
description = "Target GCP zone"
}
variable "network_name" {
type = string
description = "VPC network name"
}
variable "subnet_name" {
type = string
description = "Subnetwork name"
}
variable "cluster_name" {
type = string
description = "GKE cluster name"
}
EOF
cat << 'EOF' > main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 7.32.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 7.32.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
provider "google-beta" {
project = var.project_id
region = var.region
}
# 1. Custom Private VPC Network
resource "google_compute_network" "vpc" {
name = var.network_name
auto_create_subnetworks = false
mtu = 8896
}
# 2. Private Subnet with Secondary IP Ranges for Pods & Services
resource "google_compute_subnetwork" "subnet" {
name = var.subnet_name
ip_cidr_range = "10.10.0.0/20"
region = var.region
network = google_compute_network.vpc.id
private_ip_google_access = true
secondary_ip_range {
range_name = "gke-pods"
ip_cidr_range = "10.20.0.0/16"
}
secondary_ip_range {
range_name = "gke-services"
ip_cidr_range = "10.30.0.0/20"
}
}
# 3. Cloud Router & Cloud NAT for Secure Outbound Egress
resource "google_compute_router" "router" {
name = "${var.network_name}-router"
network = google_compute_network.vpc.id
region = var.region
}
resource "google_compute_router_nat" "nat" {
name = "${var.network_name}-nat"
router = google_compute_router.router.name
region = var.region
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}
# 4. Internal Firewall Rules
resource "google_compute_firewall" "allow_internal" {
name = "${var.network_name}-allow-internal"
network = google_compute_network.vpc.id
direction = "INGRESS"
priority = 1000
allow {
protocol = "all"
}
source_ranges = ["10.10.0.0/20", "10.20.0.0/16", "10.30.0.0/20"]
}
# 5. Private GKE Cluster with Observability and Dataplane V2
resource "google_container_cluster" "primary" {
name = var.cluster_name
location = var.region
node_locations = [var.zone]
network = google_compute_network.vpc.id
subnetwork = google_compute_subnetwork.subnet.id
initial_node_count = 1
datapath_provider = "ADVANCED_DATAPATH"
# FIX: Observability config must be nested inside monitoring_config
monitoring_config {
advanced_datapath_observability_config {
enable_metrics = true
enable_relay = true
}
}
release_channel {
channel = "RAPID"
}
ip_allocation_policy {
cluster_secondary_range_name = "gke-pods"
services_secondary_range_name = "gke-services"
}
private_cluster_config {
enable_private_nodes = true
enable_private_endpoint = false
master_ipv4_cidr_block = "172.16.0.0/28"
}
node_config {
machine_type = "e2-standard-4"
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
}
deletion_protection = false
}
# 6. Dedicated Cloud TPU v5e Node Pool (ct5lp-hightpu-4t / 2x2 Topology)
resource "google_container_node_pool" "tpu_pool" {
provider = google-beta
name = "tpu-v5e-pool"
location = var.region
node_locations = [var.zone]
cluster = google_container_cluster.primary.name
node_count = 1
node_config {
machine_type = "ct5lp-hightpu-4t"
oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
labels = {
"cloud.google.com/gke-tpu-accelerator" = "tpu-v5-lite-podslice"
"cloud.google.com/gke-tpu-topology" = "2x2"
}
taint {
key = "google.com/tpu"
value = "present"
effect = "NO_SCHEDULE"
}
}
}
EOF
- Assicurati di essere nella directory gke-cnp-lab ed esegui i seguenti comandi
terraform initInizializza la directory di lavoro. Questo è il primo passaggio e scarica i provider richiesti per la configurazione specificata.terraform plan -outgenera un piano di esecuzione, che mostra le azioni che Terraform eseguirà per eseguire il deployment della tua infrastruttura.-outconsente di salvare il piano di esecuzione in un file binario denominato. Puoi vedere cosa succederà senza apportare modifiche.terraform applyesegue gli aggiornamenti.
terraform init
terraform plan -out cnpgke
- Applica il piano di esecuzione. Poiché stai applicando il piano di esecuzione salvato, verrà eseguito immediatamente senza richiedere conferma (potrebbero essere necessari tra 6 e 10 minuti)
terraform apply cnpgke
- Verifica il deployment dell'infrastruttura:
gcloud container clusters get-credentials ${CLUSTER_NAME} --region ${REGION} --project=${PROJECT_ID}
echo -e "\n=== Verifying Cluster Nodes ==="
kubectl get nodes -L cloud.google.com/gke-tpu-accelerator,cloud.google.com/gke-tpu-topology

4. Configura il logging di rete
Prima di eseguire il deployment di workload e policy, configura GKE Network Logging per registrare la telemetria delle connessioni e controllare le decisioni relative alle policy in Cloud Logging.
- Nella directory
gke-cnp-lab, crea il manifest di configurazione NetworkLogging
cat << 'EOF' > 00-network-logging.yaml
apiVersion: networking.gke.io/v1alpha1
kind: NetworkLogging
metadata:
name: default
spec:
cluster:
allow:
delegate: false
log: true
deny:
delegate: false
log: true
EOF
- Applica la configurazione al cluster
kubectl apply -f 00-network-logging.yaml
kubectl get networklogging default
Nota: potresti visualizzare un avviso che indica che nella risorsa manca kubectl.kubernetes.io/last-applied-configuration annotation.. È del tutto normale e previsto. GKE pre-crea un oggetto NetworkLogging predefinito quando il cluster viene avviato e kubectl apply ne assume semplicemente la gestione per applicare le nuove regole.
5. Esegui il deployment del workload Gemma 4 e dei probe client
Creerai spazi dei nomi isolati per testare le valutazioni delle policy multilivello:
gemma-llm: ospita il workload di inferenza principale di Gemma 4 (tier: ai-core).client-ns: contiene il client chat RAG autorizzato (tier: app-frontend).untrusted-tenant: contiene un container di probe non autorizzato (tier: untrusted).
- In
gke-cnp-lab directory,, crea il manifest01-namespaces-clients.yamlcontenente gli spazi dei nomi e i pod client di test:
cat << 'EOF' > 01-namespaces-clients.yaml
apiVersion: v1
kind: Namespace
metadata:
name: gemma-llm
labels:
tier: ai-core
---
apiVersion: v1
kind: Namespace
metadata:
name: client-ns
labels:
tier: app-frontend
---
apiVersion: v1
kind: Namespace
metadata:
name: untrusted-tenant
labels:
tier: untrusted
---
# Authorized Client Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: rag-agent-client
namespace: client-ns
labels:
app: rag-agent
spec:
replicas: 1
selector:
matchLabels:
app: rag-agent
template:
metadata:
labels:
app: rag-agent
spec:
containers:
- name: client
image: curlimages/curl:latest
command: ["/bin/sh", "-c", "sleep 86400"]
---
# Rogue / Untrusted Probe Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: rogue-client
namespace: untrusted-tenant
labels:
app: rogue-app
spec:
replicas: 1
selector:
matchLabels:
app: rogue-app
template:
metadata:
labels:
app: rogue-app
spec:
containers:
- name: probe
image: curlimages/curl:latest
command: ["/bin/sh", "-c", "sleep 86400"]
EOF
- Applica gli spazi dei nomi e i deployment client:
kubectl apply -f 01-namespaces-clients.yaml
kubectl get namespaces gemma-llm client-ns untrusted-tenant --show-labels
Esegui il deployment del workload vLLM e Gemma
Crea il tuo secret Hugging Face. Sostituisci YOUR_ACTUAL_HUGGING_FACE_TOKEN con il tuo token:
export HF_TOKEN="YOUR_ACTUAL_HUGGING_FACE_TOKEN"
echo $HF_TOKEN
kubectl create secret generic hf-secret -n gemma-llm --from-literal=hf_token=${HF_TOKEN}
- Crea il manifest del workload di inferenza Gemma 4 che ha come target Cloud TPU v5e utilizzando vllm-tpu:
cat << 'EOF' > 02-gemma4-tpu-vllm.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gemma-serving
namespace: gemma-llm
labels:
app: gemma-service
spec:
replicas: 1
selector:
matchLabels:
app: gemma-service
template:
metadata:
labels:
app: gemma-service
ai.gke.io/model: gemma4
ai.gke.io/inference-server: vllm-tpu
spec:
tolerations:
- key: "google.com/tpu"
operator: "Exists"
effect: "NoSchedule"
nodeSelector:
cloud.google.com/gke-tpu-accelerator: tpu-v5-lite-podslice
cloud.google.com/gke-tpu-topology: 2x2
containers:
- name: vllm-inference
image: vllm/vllm-tpu:latest
command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
args:
- "--model=google/gemma-4-E2B-it"
- "--tensor-parallel-size=4"
- "--host=0.0.0.0"
- "--port=8000"
- "--max-model-len=4096"
- "--max-num-batched-tokens=4096"
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: hf_token
- name: HUGGING_FACE_HUB_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: hf_token
volumeMounts:
- mountPath: /dev/shm
name: dshm
ports:
- name: http-inference
containerPort: 8000
resources:
requests:
google.com/tpu: "4"
memory: "32Gi"
cpu: "8"
limits:
google.com/tpu: "4"
memory: "32Gi"
cpu: "8"
startupProbe:
httpGet:
path: /health
port: 8000
failureThreshold: 120
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 5
volumes:
- name: dshm
emptyDir:
medium: Memory
---
apiVersion: v1
kind: Service
metadata:
name: gemma-service
namespace: gemma-llm
spec:
type: ClusterIP
selector:
app: gemma-service
ports:
- name: http
protocol: TCP
port: 8000
targetPort: 8000
EOF
- Crea il deployment.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
- Per monitorare lo stato di completamento, esegui lo script riportato di seguito. I pod attenderanno il provisioning del nodo prima di poter procedere. L'operazione potrebbe richiedere più di 13 minuti.
echo " __|__"
echo " --@--(_|_)--@--"
echo ""
echo "Waiting for Gemma 4 TPU Pod to initialize and become Ready..."
# 1. Wait for the pod to be scheduled and reach the Running state
kubectl wait -n gemma-llm --for=condition=Ready pod -l app=gemma-service --timeout=600s
echo ""
echo "✅ Pod is Running! Streaming vLLM engine initialization logs..."
# 2. Stream logs until Uvicorn/vLLM is ready to serve
kubectl logs -n gemma-llm -l app=gemma-service -c vllm-inference -f | sed -u '/Application startup complete/q'
echo ""
echo "🚀 Gemma 4 on TPU v5e is ready for inference!"
6. Informazioni sulla valutazione delle policy e sullo stato predefinito
Prima di applicare le policy di rete, scopri come GKE ClusterNetworkPolicy e Kubernetes NetworkPolicy standard interagiscono tra diversi livelli di valutazione.
Il traffico in GKE viene valutato tramite un modello di precedenza a più livelli:
Admin Tier ⟶ NetworkPolicy Tier ⟶ Baseline Tier ⟶ Default GKE Behavior

Il nostro flusso di test
===================================================
4-STAGE POLICY EVALUATION FLOW
===================================================
[Stage 1: Default State]
├── Pre-Policy State: Default Kubernetes allow-all
├── Ingress: client-ns ──▶ Gemma 4: ALLOWED (200 OK)
├── Ingress: untrusted ──▶ Gemma 4: ALLOWED (200 OK)
├── Egress: All Pods ──▶ Metadata: ALLOWED (Exposed)
└── Security Posture: ⚠️ Overly permissive
│
▼
[Stage 2: Baseline Lockdown]
├── Policy: ClusterNetworkPolicy (tier: Baseline)
├── Rule: Default-deny all cross-namespace ingress
├── Ingress: client-ns ──▶ Gemma 4: DROPPED (Timeout)
├── Ingress: untrusted ──▶ Gemma 4: DROPPED (Timeout)
└── Security Posture: 🛑 Zero-trust baseline active
│
▼
[Stage 3: Developer Delegation]
├── Policy: NetworkPolicy (tier: NetworkPolicy)
├── Rule: App team allows ingress from client-ns
├── Ingress: client-ns ──▶ Gemma 4: ALLOWED (200 OK)
├── Ingress: untrusted ──▶ Gemma 4: DROPPED (Timeout)
└── Security Posture: 🔀 App isolation achieved
│
▼
[Stage 4: Admin Guardrail]
├── Policy: ClusterNetworkPolicy (tier: Admin)
├── Rule: Cluster-wide metadata drop (169.254.169.254)
├── Egress: All Pods ──▶ Metadata: DROPPED (Timeout)
├── Dev NetPol bypass attempt: OVERRIDDEN (Blocked)
└── Security Posture: 🔒 Enforced guardrail
===================================================
Stabilisci la base di riferimento (test pre-policy)
Verifica che il modello Gemma 4 sia integro e gestisca le richieste di inferenza. In Kubernetes standard senza policy, tutto il traffico tra spazi dei nomi è implicitamente consentito.
- Testa l'inferenza dal client autorizzato in
client-ns:
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- Testa l'inferenza dal client non autorizzato in
untrusted-tenant:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- Verifica se i pod in tutti e tre gli spazi dei nomi possono raggiungere il server dei metadati (169.254.169.254):
echo -e "\n[1/3] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[2/3] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[3/3] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
7. Test di valutazione di base e livello di policy di rete
In questo passaggio, in qualità di amministratore del cluster, applicherai un livello di base ClusterNetworkPolicy per stabilire una strategia di negazione predefinita di riserva a livello di cluster. Qualsiasi comunicazione tra spazi dei nomi non esplicitamente consentita da un livello superiore verrà ignorata.
Stabilisci una postura Zero Trust (livello di base)
- Crea il manifest della policy
03-baseline-deny.yamlBaseline-tier:
cat << 'EOF' > 03-baseline-deny.yaml
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: baseline-default-deny
spec:
tier: Baseline
priority: 1000
subject:
namespaces: {}
ingress:
# 1. Allow CoreDNS Ingress cluster-wide so DNS resolution never breaks
- name: allow-dns-ingress
action: Accept
from:
- namespaces: {}
protocols:
- udp:
destinationPort:
number: 53
- tcp:
destinationPort:
number: 53
# 2. Default Deny all other cross-namespace ingress
- name: baseline-deny-cross-ns
action: Deny
from:
- namespaces: {}
EOF
- Applica la policy del livello Baseline:
kubectl apply -f 03-baseline-deny.yaml
- Testa la connettività del client da
client-ns.
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- Testa l'inferenza dal client canaglia non autorizzato in untrusted-tenant:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
Delega l'accesso agli sviluppatori (livello NetworkPolicy)
Il team dell'applicazione gemma-llm applicherà ora una NetworkPolicy standard con ambito dello spazio dei nomi per concedere il traffico autorizzato da client-ns.
- Crea il manifest NetworkPolicy per lo sviluppatore
04-app-networkpolicy.yaml:
cat << 'EOF' > 04-app-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-rag-client
namespace: gemma-llm
spec:
podSelector:
matchLabels:
app: gemma-service
policyTypes:
- Ingress
ingress:
# Explicitly permit ingress only from pods matching app: rag-agent in client-ns
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: client-ns
podSelector:
matchLabels:
app: rag-agent
ports:
- protocol: TCP
port: 8000
EOF
- Applica la policy:
kubectl apply -f 04-app-networkpolicy.yaml
- Testa la connettività del client da
client-ns
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- Testa la connettività dal client non autorizzato in
untrusted-tenant:
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
8. Applicazione della valutazione dei guardrail globali (livello amministratore)
In qualità di amministratore del cluster,ora applicherai un livello amministratore ClusterNetworkPolicy nella parte superiore dell'ordine di valutazione di GKE. Questo livello viene valutato per primo e qualsiasi regola corrispondente ha la precedenza immediata su tutti gli altri livelli. Se non viene trovata alcuna corrispondenza, il pacchetto viene passato ai livelli inferiori (NetworkPolicy e Baseline) per essere valutato.
In questo passaggio, applicherai una policy che:
- Guardrail in uscita a livello di cluster: impedisce a tutti i pod in ogni namespace di raggiungere il server dei metadati di Google Cloud (
169.254.169.254/32). - Supporto DNS: consente tutto il traffico DNS sulla porta 53 in modo che i pod possano risolvere i nomi host (tenendo conto delle cache DNS locali di GKE).
- Delega del traffico in entrata e in uscita: passa tutte le altre decisioni sul traffico utilizzando l'azione Passa, in modo che le policy di rete standard degli sviluppatori e le regole di negazione predefinite di base possano prendere le decisioni finali.
- Crea il manifest della policy del livello amministratore
05-admin-guardrails.yamlche ha come target tutti gli spazi dei nomi
cat << 'EOF' > 05-admin-guardrails.yaml
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: admin-global-guardrails
spec:
tier: Admin
priority: 10
subject:
namespaces: {}
ingress:
- name: delegate-all-ingress
action: Pass
from:
- namespaces: {}
egress:
- name: allow-all-dns
action: Accept
to:
- networks:
- 0.0.0.0/0
protocols:
- udp:
destinationPort:
number: 53
- tcp:
destinationPort:
number: 53
- name: block-metadata-server-global
action: Deny
to:
- networks:
- 169.254.169.254/32
- name: delegate-other-egress
action: Pass
to:
- namespaces: {}
EOF
- Applica la policy del livello Amministratore:
kubectl apply -f 05-admin-guardrails.yaml
- Testa l'esfiltrazione dei metadati in tutti e tre i namespace:
echo -e "\n[1/3] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[2/3] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n[3/3] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Blocked)"
- Testa la connettività del client da
client-ns
kubectl exec -n client-ns deploy/rag-agent-client -- curl --connect-timeout 3 -s -X POST \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-4-E2B-it",
"messages": [{"role": "user", "content": "Explain zero trust in one short sentence."}],
"max_tokens": 40
}' | jq -r '.choices[0].message.content'
- Simula un tentativo di bypass da parte di uno sviluppatore applicando un NetworkPolicy di traffico in uscita che consente tutto all'interno di gemma-llm:
cat << 'EOF' > 06-bypass-attempt.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-allow-all-egress
namespace: gemma-llm
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {}
EOF
kubectl apply -f 06-bypass-attempt.yaml
- Verifica se il bypass per sviluppatori può sbloccare l'accesso ai metadati:
kubectl exec -n gemma-llm deploy/gemma-serving -- curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo "🛑 RESULT: Connection Timed Out (Admin Guardrail Overrode Developer Policy)"
9. Verifica interattiva con Hubble e Cloud Logging
Per osservare l'applicazione delle policy in tempo reale e le decisioni di controllo dei pacchetti nel cluster, utilizzerai una sessione di Cloud Shell a doppio riquadro che combina il tracciamento dei pacchetti eBPF a livello di kernel con l'esecuzione dei test automatizzati.
- Fai clic sull'icona "+" nel menu di Cloud Shell per aprire una seconda scheda del terminale.
- In questa nuova scheda, esegui i seguenti comandi per creare l'alias e iniziare a osservare il traffico in tempo reale per lo spazio dei nomi
gemma-llm:
# Create an alias to execute Hubble natively inside the relay pod
alias hubble="kubectl exec -it -n gke-managed-dpv2-observability deploy/hubble-relay -c hubble-cli -- hubble"
# Observe real-time traffic to and from the Gemma namespace
hubble observe --follow --namespace gemma-llm
- Torna alla prima scheda di Cloud Shell e assicurati di trovarti nella directory
gke-cnp-lab.
cd ~/gke-cnp-lab
- Esegui il blocco seguente per creare ed eseguire lo script di verifica automatica. Questo script esegue i test del traffico richiesti e interroga Cloud Logging utilizzando lo schema esatto dei metadati a livello di root di GKE per visualizzare le regole esatte di Dataplane V2 che hanno valutato le richieste:
cat << 'EOF' > verify-suite.sh
#!/usr/bin/env bash
set -e
echo "========================================================"
echo " 🧪 RUNNING MULTI-TIER POLICY VERIFICATION SUITE"
echo "========================================================"
# Test 1: Ingress Authorized Client -> Gemma (Allowed by NetworkPolicy)
echo -e "\n[1/5] Testing Authorized Client -> Gemma Serving..."
if kubectl exec -n client-ns deploy/rag-agent-client -- \
curl -s --connect-timeout 3 \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
echo " ✅ RESULT: FORWARDED (Allowed by Developer Network Policy)"
else
echo " 🛑 RESULT: FAILED"
fi
# Test 2: Ingress Rogue Client -> Gemma (Blocked by Baseline Tier)
echo -e "\n[2/5] Testing Unauthorized Rogue Tenant -> Gemma Serving..."
if kubectl exec -n untrusted-tenant deploy/rogue-client -- \
curl -s --connect-timeout 3 \
http://gemma-service.gemma-llm.svc.cluster.local:8000/v1/chat/completions > /dev/null; then
echo " ⚠️ RESULT: UNEXPECTED ALLOW"
else
echo " 🛑 RESULT: DROPPED (Blocked by Baseline Tier)"
fi
# Test 3: Egress Gemma -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[3/5] Testing Metadata access from Gemma serving pod (gemma-llm)..."
kubectl exec -n gemma-llm deploy/gemma-serving -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
# Test 4: Egress Authorized Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[4/5] Testing Metadata access from authorized client (client-ns)..."
kubectl exec -n client-ns deploy/rag-agent-client -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
# Test 5: Egress Rogue Client -> GCP Metadata Server (Blocked by Admin Tier)
echo -e "\n[5/5] Testing Metadata access from rogue client (untrusted-tenant)..."
kubectl exec -n untrusted-tenant deploy/rogue-client -- \
curl --connect-timeout 3 -s -w "\nHTTP Code: %{http_code}\n" -H "Metadata-Flavor: Google" \
http://169.254.169.254/computeMetadata/v1/instance/zone || echo " 🛑 RESULT: Connection Timed Out (Blocked)"
echo -e "\n========================================================"
echo " 📋 FETCHING AUDIT TRAIL FROM CLOUD LOGGING"
echo "========================================================"
echo "Waiting 12 seconds for telemetry ingestion..."
sleep 12
echo -e "\n🔍 1. Validating Baseline Tier (Rogue Client Block):"
gcloud logging read '
resource.type="k8s_node"
logName=~"logs/policy-action"
jsonPayload.disposition="deny"
jsonPayload.src.namespace="untrusted-tenant"
jsonPayload.dest.namespace="gemma-llm"
' --limit=1 --format="table(
timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
jsonPayload.src.namespace:label=SRC_NS,
jsonPayload.dest.namespace:label=DEST_NS,
jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"
echo -e "\n🔍 2. Validating Admin Tier (Metadata Block across all namespaces):"
gcloud logging read '
resource.type="k8s_node"
logName=~"logs/policy-action"
jsonPayload.disposition="deny"
jsonPayload.connection.dest_ip="169.254.169.254"
' --limit=3 --format="table(
timestamp.date('%H:%M:%S', tz=LOCAL):label=TIME,
jsonPayload.src.namespace:label=SRC_NS,
jsonPayload.connection.dest_ip:label=DEST_IP,
jsonPayload.policies[0].name:label=BLOCKED_BY_POLICY
)"
EOF
chmod +x verify-suite.sh
./verify-suite.sh
- Mentre lo script viene eseguito nella prima scheda, osserva l'output di Hubble nella seconda scheda.
In Hubble (scheda 2): vedrai le richieste HTTP autorizzate INOLTRATE in verde. Per il traffico bloccato, vedrai eventi rossi (DROPPED). Il tenant canaglia: la connessione viene trasmessa dal livello Amministratore e bloccata al livello Baseline. La query dei metadati: la sua richiesta di accesso alle credenziali cloud viene bloccata immediatamente a livello di amministratore.

In Cloud Logging (scheda 1): lo script termina estraendo i record di controllo esatti inviati a Google Cloud Logging. In questo modo viene dimostrato esplicitamente quale livello (Admin o Baseline) e quale policy hanno bloccato il traffico non autorizzato, con timestamp formattati in base al fuso orario locale.

10. Esegui la pulizia
- Chiudi la seconda scheda
- Torna alla prima scheda di Cloud Shell.
- Elimina i manifest, gli spazi dei nomi e i secret Kubernetes di cui è stato eseguito il deployment nell'ordine seguente:
cd ~/gke-cnp-lab
# Delete policy and verification scripts
rm -f verify-suite.sh
kubectl delete -f 05-admin-guardrails.yaml --ignore-not-found
kubectl delete -f 04-app-networkpolicy.yaml --ignore-not-found
kubectl delete -f 03-baseline-deny.yaml --ignore-not-found
kubectl delete -f 00-network-logging.yaml --ignore-not-found
# Delete workload deployments, secrets, and namespaces
kubectl delete -f 02-gemma4-tpu-vllm.yaml --ignore-not-found
kubectl delete secret hf-secret -n gemma-llm --ignore-not-found
kubectl delete -f 01-namespaces-clients.yaml --ignore-not-found
- Esegui la pulizia dell'infrastruttura con il comando seguente, digita
yesper confermare
terraform destroy --auto-approve
- Verifica che tutte le risorse siano state terminate correttamente nel tuo progetto attivo:
gcloud container clusters list --region=us-west4
gcloud compute networks list
- Elimina la directory
gke-cnp-labe i relativi contenuti:
cd
rm -r gke-cnp-lab
11. Complimenti
Hai completato il codelab Proteggi i workload di inferenza AI con ClusterNetworkPolicy di GKE.
Completando questo lab pratico, hai progettato e convalidato un'architettura di rete di difesa in profondità multilivello solida per la gestione di workload LLM privati sensibili su Google Kubernetes Engine.
Cosa hai realizzato:
- Infrastructure as Code: è stato eseguito il provisioning dinamico di un cluster GKE standard privato altamente sicuro, di una subnet regionale privata e di un hardware Cloud TPU v5e dedicato utilizzando Terraform.
- Telemetria e osservabilità: configurazione del logging di rete di Dataplane V2 insieme al tracciamento Hubble eBPF a livello di kernel per ottenere una visibilità approfondita dei pacchetti di runtime.
- Security posture gerarchica:mostra una chiara separazione dei compiti operativi in tre livelli di valutazione distinti:
- Livello amministratore: applicazione di guardrail di uscita non ignorabili a livello di cluster che bloccano l'accesso laterale alle credenziali dei metadati (169.254.169.254).
- Livello NetworkPolicy: confini di ingresso con ambito dello spazio dei nomi delegati agli sviluppatori di applicazioni per autorizzare in sicurezza le connessioni client di produzione.
- Livello di base: abbiamo creato una rete di sicurezza di riserva a livello di cluster che nega per impostazione predefinita il traffico dei tenant non autorizzati.
- Controllo in tempo reale: esegui una suite di test di verifica completa, osserva i pacchetti eliminati in tempo reale a livello di kernel Linux e interroga i controlli strutturati delle azioni delle policy direttamente in Google Cloud Logging
Prossimi passi/Scopri di più
Puoi scoprire di più sul networking GKE
- Approfondimento sulla configurazione di ClusterNetworkPolicy di GKE