1. Visão geral
Neste laboratório, você vai conhecer a ClusterNetworkPolicy no Google Kubernetes Engine (GKE). A ClusterNetworkPolicy é aplicada a vários casos de uso e pode ser aplicada em todo o cluster e aplicada nos níveis de administrador e de referência. Neste exemplo, vamos demonstrar o controle com uma implantação de LLM (Gemma) no GKE com TPUs, vLLM e validação.
Você vai trabalhar com as seguintes tecnologias:
- Google Kubernetes Engine (GKE): a plataforma básica de orquestração de contêineres para hospedar cargas de trabalho particulares e escalonáveis.
- ClusterNetworkPolicy (CNP): políticas de rede de vários níveis com escopo de cluster que permitem proteções de SecOps nos níveis Admin, NetworkPolicy e Baseline.
- Mecanismo de inferência de TPU do vLLM: inferência de alta capacidade de processamento que exibe o modelo leve Gemma 4 (google/gemma-4-E2B-it) em TPUs do Google com endpoints REST compatíveis com a OpenAI.
Papéis e personas
A API de política de rede é estruturada em torno de personas organizacionais específicas:
- Uso do administrador do cluster (
ClusterNetworkPolicy): aplica proteções de segurança nos clusters nos níveis de administrador e de referência. - Uso do administrador de namespace (
NetworkPolicy): gerencia os limites e o isolamento no nível do locatário em namespaces específicos. - Uso do desenvolvedor de aplicativos (
NetworkPolicy): define regras detalhadas para microsserviços específicos do aplicativo.
Para configurar esse ambiente, você vai implantar uma VPC particular personalizada, o Cloud NAT e um cluster do GKE particular com um pool de nós de TPU dedicado.
Para proteger o cluster, você vai criar recursos ClusterNetworkPolicy de vários níveis com objetos NetworkPolicy padrão para aplicar proteções de rede rigorosas. Em seguida,você vai implantar uma carga de trabalho de IA que usa o vLLM e as Cloud TPUs do Google Cloud para disponibilizar o modelo Gemma 4.
Por fim, você vai estabelecer uma referência de trabalho, aplicar as políticas de segurança e configurar o NetworkLogging usando o Cloud Logging e streams de observabilidade do Hubble eBPF em tempo real para auditar essas decisões baseadas em política e verificar as perdas de pacotes. Todas as configurações vão usar uma combinação de Terraform, gcloud e kubectl..
Neste laboratório, você vai aprender a executar as seguintes tarefas:
- Provisionar uma rede privada, um cluster do GKE e aceleradores do Cloud TPU v5e usando o Terraform.
- Configurar o NetworkLogging do GKE para registrar a telemetria de conexão.
- Implantar o Gemma 4 E2B na TPU usando a imagem de contêiner oficial vllm/vllm-tpu.
- Estabeleça uma referência de trabalho que comprove a conectividade de ponta a ponta antes de aplicar restrições.
- Aplique uma ClusterNetworkPolicy
Baseline-tierpara negar por padrão o tráfego não autorizado entre namespaces. - Aplicar uma NetworkPolicy gerenciada pelo desenvolvedor para delegar acesso a clientes autorizados.
- Aplique uma ClusterNetworkPolicy
Admin-tierpara aplicar proteções de saída globais que substituem as configurações do desenvolvedor. - Verificar as quedas e as passagens de pacotes em tempo real usando (streams do Hubble eBPF e Cloud Logging).
Neste laboratório, você vai criar o seguinte padrão.
Figura 1. 
2. Configuração dos serviços do Google Cloud
Configuração de ambiente autoguiada
- Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.



- O Nome do projeto é o nome de exibição para os participantes do projeto. É uma string de caracteres não usada pelas APIs do Google e pode ser atualizada quando você quiser.
- O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser mudado após a definição. O console do Cloud gera automaticamente uma string exclusiva. Em geral, não importa o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, normalmente identificado como
PROJECT_ID. Se você não gostar do ID gerado, crie outro aleatório. Se preferir, teste o seu e confira se ele está disponível. Ele não pode ser mudado após essa etapa e permanece durante o projeto. - Para sua informação, há um terceiro valor, um Número do projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
- Em seguida, ative o faturamento no console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não vai ser muito cara, se tiver algum custo. Para encerrar os recursos e evitar cobranças além deste tutorial, exclua os recursos criados ou exclua o projeto. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.
Inicie o Cloud Shell
Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.
No Console do Google Cloud, clique no ícone do Cloud Shell na barra de ferramentas superior à direita:

O provisionamento e a conexão com o ambiente levarão apenas alguns instantes para serem concluídos: Quando o processamento for concluído, você verá algo como:

Essa máquina virtual contém todas as ferramentas para desenvolvedores necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Neste codelab, todo o trabalho pode ser feito com um navegador. Você não precisa instalar nada.
3. Configurar o ambiente com o Terraform
Para concluir este laboratório,
- Você precisa ter acesso à cota da TPU v5e na região e zona selecionadas, por exemplo,
us-central1-a, us-west4-a, us-south1-a, us-west1-c, europe-west4-b. Siga o documento do plano da TPU e ative a cota da TPU para ter acesso. - Essa é uma implantação pequena que requer quatro chips de TPU v5e (
ct5lp-hightpu-4t),), que vão formar uma fração 2x2 em uma única zona.
Para usar o Hugging Face
- Token do Hugging Face:é necessário um token de acesso para fazer o download dos pesos do modelo Gemma.
- Aceite os contratos de uso, conforme necessário.
Vamos criar uma VPC personalizada com regras de firewall, uma sub-rede e um cluster do Autopilot. Abra o console do Cloud e selecione o projeto que você vai usar.
- Abra o Cloud Shell, localizado na parte de cima do console à direita, verifique se o ID do projeto correto aparece no Cloud Shell e confirme todos os comandos para permitir o acesso.

- Crie um diretório chamado
gke-cnp-lab,e acesse-o.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
- Agora crie algumas variáveis. Observação: edite REGION e ZONE para refletir o local real em que você tem acesso aos recursos da TPU. O padrão usado na região
us-west4e na zonaus-west4-ado laboratório.
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"
- Salve as variáveis de configuração em um script auxiliar no diretório gke-cnp-lab. Se a sessão do Cloud Shell for desconectada ou redefinida, você poderá restaurar rapidamente as variáveis ativas executando
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
- Agora adicione alguns arquivos de configuração. Isso vai criar os arquivos terraform.tfvars, variables.tf e main.tf no diretório 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
- Verifique se você está no diretório gke-cnp-lab e execute os seguintes comandos
terraform initpara inicializar o diretório de trabalho. Essa é a primeira etapa, que faz o download dos provedores necessários para a configuração em questão.terraform plan -outgera um plano de execução, mostrando quais ações o Terraform vai realizar para implantar sua infraestrutura. O-outpermite salvar o plano de execução em um binário nomeado. Você pode ver o que vai acontecer sem fazer mudanças.terraform applyexecuta as atualizações.
terraform init
terraform plan -out cnpgke
- Aplique o plano de execução. Como você está aplicando o plano de execução salvo, ele será executado imediatamente sem pedir confirmação (isso pode levar de 6 a 10 minutos).
terraform apply cnpgke
- Verifique a implantação da infraestrutura:
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. Configurar a geração de registros de rede
Antes de implantar cargas de trabalho e políticas, configure o registro de rede do GKE para registrar a telemetria de conexão e as decisões de política de auditoria no Cloud Logging.
- No diretório
gke-cnp-lab, crie o manifesto de configuração 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
- Aplicar a configuração ao cluster
kubectl apply -f 00-network-logging.yaml
kubectl get networklogging default
Observação: talvez você receba um aviso informando que o recurso não tem o kubectl.kubernetes.io/last-applied-configuration annotation.. Isso é completamente normal e esperado. O GKE pré-cria um objeto NetworkLogging padrão quando o cluster é iniciado, e o kubectl apply simplesmente assume o gerenciamento dele para aplicar as novas regras.
5. Implantar a carga de trabalho do Gemma 4 e as sondagens do cliente
Você vai criar namespaces isolados para testar avaliações de políticas em vários níveis:
gemma-llm: abriga a carga de trabalho principal de inferência do Gemma 4 (tier: ai-core).client-ns: abriga o cliente de chat de RAG autorizado (tier: app-frontend).untrusted-tenant: abriga um contêiner de sondagem não autorizado (tier: untrusted).
- No
gke-cnp-lab directory,, crie o manifesto01-namespaces-clients.yamlque contém os namespaces e os pods de cliente de teste:
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
- Aplique os namespaces e as implantações do cliente:
kubectl apply -f 01-namespaces-clients.yaml
kubectl get namespaces gemma-llm client-ns untrusted-tenant --show-labels
Implantar a carga de trabalho vLLM e Gemma
Crie o secret do Hugging Face. Substitua YOUR_ACTUAL_HUGGING_FACE_TOKEN pelo seu 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}
- Crie o manifesto da carga de trabalho de inferência do Gemma 4 para o Cloud TPU v5e usando 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
- Crie a implantação.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
- Para monitorar o status de conclusão, execute o script abaixo. Os pods vão esperar até que o nó seja provisionado antes de continuar. Isso pode levar mais de 13 minutos.
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. Noções básicas sobre a avaliação de políticas e o estado padrão
Antes de aplicar políticas de rede, entenda como o ClusterNetworkPolicy do GKE e o NetworkPolicy padrão do Kubernetes interagem em diferentes níveis de avaliação.
O tráfego no GKE é avaliado por um modelo de precedência de vários níveis:
Admin Tier ⟶ NetworkPolicy Tier ⟶ Baseline Tier ⟶ Default GKE Behavior

Nosso fluxo de teste
===================================================
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
===================================================
Estabelecer a base de referência (teste pré-política)
Verifique se o modelo Gemma 4 está íntegro e atendendo às solicitações de inferência. No Kubernetes padrão sem políticas, todo o tráfego entre namespaces é implicitamente permitido.
- Teste a inferência do cliente autorizado em
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'
- Teste a inferência do cliente invasor não autorizado em
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'
- Teste se os pods nos três namespaces podem acessar o servidor de metadados (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. Teste de avaliação da configuração de referência e do nível da política de rede
Nesta etapa, como administrador do cluster, você vai aplicar um ClusterNetworkPolicy de nível de referência para estabelecer uma postura de fallback de negação padrão em todo o cluster. Qualquer comunicação entre namespaces que não seja explicitamente permitida por um nível superior será descartada.
Estabelecer uma postura de confiança zero (nível de referência)
- Crie o manifesto de política do nível de referência do
03-baseline-deny.yaml:
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
- Aplique a política do nível de referência:
kubectl apply -f 03-baseline-deny.yaml
- Testar a conectividade do cliente em
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'
- Teste a inferência do cliente invasor não autorizado em 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'
Delegar acesso a desenvolvedores (nível NetworkPolicy)
A equipe do aplicativo gemma-llm agora vai aplicar uma NetworkPolicy padrão com escopo de namespace para conceder tráfego autorizado de client-ns.
- Crie o manifesto
04-app-networkpolicy.yamlNetworkPolicy do desenvolvedor:
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
- Aplique a política:
kubectl apply -f 04-app-networkpolicy.yaml
- Testar a conectividade do cliente em
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'
- Teste a conectividade do cliente invasor em
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. A avaliação aplica as proteções globais (nível de administrador)
Como administrador do cluster,você vai aplicar um de nível de administradorClusterNetworkPolicy no topo da ordem de avaliação do GKE. Esse nível é avaliado primeiro, e qualquer regra correspondente tem precedência imediata sobre todos os outros níveis. Se não houver correspondência, o pacote será transmitido para ser avaliado pelos níveis inferiores (NetworkPolicy e Baseline).
Nesta etapa, você vai aplicar uma política que:
- Proteção de saída em todo o cluster: impede que todos os pods em todos os namespaces acessem o servidor de metadados do Google Cloud (
169.254.169.254/32). - Suporte a DNS: permite todo o tráfego de DNS na porta 53 para que os pods possam resolver nomes de host (considerando os caches de DNS locais do GKE).
- Delegação de entrada e saída: transmite todas as outras decisões de tráfego usando a ação "Passar" para que as políticas de rede padrão do desenvolvedor e as regras de negação padrão de referência possam tomar as decisões finais.
- Crie o manifesto da política de nível de administrador
05-admin-guardrails.yamlque segmenta todos os namespaces.
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
- Aplique a política de nível de administrador:
kubectl apply -f 05-admin-guardrails.yaml
- Teste a exfiltração de metadados nos três namespaces:
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)"
- Testar a conectividade do cliente em
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'
- Simule uma tentativa de desvio do desenvolvedor aplicando uma NetworkPolicy de saída que permite tudo dentro de 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
- Teste se o desvio do desenvolvedor pode desbloquear o acesso aos metadados:
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ção interativa com o Hubble e o Cloud Logging
Para observar a aplicação de políticas em tempo real e as decisões de auditoria de pacotes em todo o cluster, você vai usar uma sessão do Cloud Shell de painel duplo que combina o rastreamento de pacotes eBPF no nível do kernel com a execução de testes automatizados.
- Clique no ícone "+" no menu do Cloud Shell para abrir uma segunda guia do terminal.
- Nessa nova guia, execute os comandos a seguir para criar o alias e começar a observar o tráfego em tempo real do namespace
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
- Volte para a primeira guia do Cloud Shell e verifique se você está no diretório
gke-cnp-lab.
cd ~/gke-cnp-lab
- Execute o bloco a seguir para criar e executar o script de verificação automatizada. Esse script executa os testes de tráfego necessários e consulta o Cloud Logging usando o esquema de metadados exato do GKE no nível raiz para exibir as regras exatas do Dataplane V2 que avaliaram as solicitações:
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
- Enquanto o script é executado na primeira guia, observe a saída do Hubble na segunda.
No Hubble (guia 2): as solicitações HTTP autorizadas vão aparecer como ENCAMINHADAS em verde. Para o tráfego bloqueado, você vai ver eventos vermelhos (DESCARTADOS). O locatário invasor: a conexão é transmitida pelo nível de administrador e bloqueada no nível de referência. A consulta de metadados: a solicitação para acessar as credenciais da nuvem é bloqueada imediatamente no nível de administrador.

No Cloud Logging (guia 1): o script é concluído ao extrair os registros de auditoria exatos enviados ao Google Cloud Logging. Isso prova explicitamente qual nível (administrador ou valor de referência) e qual política foram responsáveis por interromper o tráfego não autorizado, formatado com carimbos de data/hora localizados e que consideram o fuso horário.

10. Limpar
- Feche a segunda guia.
- Volte para a primeira guia do Cloud Shell.
- Exclua os manifestos, namespaces e secrets do Kubernetes implantados na ordem:
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
- Limpe a infraestrutura com o comando a seguir e digite
yespara confirmar.
terraform destroy --auto-approve
- Verifique se todos os recursos foram encerrados corretamente no projeto ativo:
gcloud container clusters list --region=us-west4
gcloud compute networks list
- Exclua o diretório
gke-cnp-labe o conteúdo dele:
cd
rm -r gke-cnp-lab
11. Parabéns
Você concluiu o codelab "Proteger cargas de trabalho de inferência de IA com a ClusterNetworkPolicy do GKE".
Ao concluir este laboratório prático, você projetou e validou uma arquitetura de rede de defesa em profundidade robusta e de várias camadas para disponibilizar cargas de trabalho de LLM particulares e sensíveis no Google Kubernetes Engine.
O que você realizou:
- Infraestrutura como código:provisionou um cluster do GKE Standard particular altamente seguro, uma sub-rede regional particular e hardware dedicado do Cloud TPU v5e de forma dinâmica usando o Terraform.
- Telemetria e observabilidade:configuramos o registro de rede do Dataplane V2 com o rastreamento do Hubble eBPF no nível do kernel para ter uma visibilidade detalhada dos pacotes de ambiente de execução.
- Postura de segurança hierárquica:por exemplo, uma separação clara de funções operacionais em três níveis de avaliação distintos:
- Nível de administrador:proteções de saída não ignoráveis aplicadas em todo o cluster que bloqueiam o acesso lateral a credenciais de metadados (169.254.169.254).
- Nível NetworkPolicy:limites de entrada delegados com escopo de namespace para desenvolvedores de aplicativos autorizarem conexões de clientes de produção com segurança.
- Nível básico:criamos uma rede de segurança de fallback para todo o cluster que nega por padrão o tráfego de locatários não autorizados.
- Auditoria em tempo real:execute um pacote completo de testes de verificação, observe as quedas de pacotes em tempo real no nível do kernel do Linux e consulte auditorias estruturadas de ações de política diretamente no Google Cloud Logging.
Próximas etapas / Saiba mais
Leia mais sobre a rede do GKE.
- Análise detalhada de Como configurar a ClusterNetworkPolicy do GKE