Proteger cargas de trabalho com a ClusterNetworkPolicy do GKE: exemplo de inferência de IA (Gemma, TPU)

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:

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-tier para 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-tier para 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. dbe5e5f7b35afb8e.jpeg

2. Configuração dos serviços do Google Cloud

Configuração de ambiente autoguiada

  1. 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.

295004821bab6a87.png37d264871000675d.png96d86d3d5655cdbe.png

  • 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.
  1. 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:

Ativar o Cloud Shell

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:

Captura de tela do terminal do Google Cloud Shell mostrando que o ambiente foi conectado

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.

  1. 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. b51b80043d3bac90.png
  2. Crie um diretório chamado gke-cnp-lab, e acesse-o.
mkdir -p gke-cnp-lab && cd gke-cnp-lab
  1. 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-west4 e na zona us-west4-a do 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"
  1. 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
  1. 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
  1. Verifique se você está no diretório gke-cnp-lab e execute os seguintes comandos
    terraform init para 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 -out gera um plano de execução, mostrando quais ações o Terraform vai realizar para implantar sua infraestrutura. O -out permite salvar o plano de execução em um binário nomeado. Você pode ver o que vai acontecer sem fazer mudanças.
    terraform apply executa as atualizações.
terraform init 
terraform plan -out cnpgke 
  1. 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
  1. 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

8b187e355548b7c5.png

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.

  1. 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
  1. 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).
  1. No gke-cnp-lab directory,, crie o manifesto 01-namespaces-clients.yaml que 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}
  1. 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
  1. Crie a implantação.
kubectl apply -f 02-gemma4-tpu-vllm.yaml
  1. 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

d4cf83398c69b6b8.png

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.

  1. 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'
  1. 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'
  1. 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)

  1. 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
  1. Aplique a política do nível de referência:
kubectl apply -f 03-baseline-deny.yaml
  1. 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'
  1. 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.

  1. Crie o manifesto 04-app-networkpolicy.yaml NetworkPolicy 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
  1. Aplique a política:
kubectl apply -f 04-app-networkpolicy.yaml
  1. 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'
  1. 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.
  1. Crie o manifesto da política de nível de administrador 05-admin-guardrails.yaml que 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
  1. Aplique a política de nível de administrador:
kubectl apply -f 05-admin-guardrails.yaml
  1. 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)"
  1. 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'
  1. 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
  1. 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.

  1. Clique no ícone "+" no menu do Cloud Shell para abrir uma segunda guia do terminal.
  2. 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
  1. Volte para a primeira guia do Cloud Shell e verifique se você está no diretório gke-cnp-lab.
cd ~/gke-cnp-lab
  1. 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
  1. 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.

7d68cc0e9b90f820.png

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.

b9e48fd473451ca2.png

10. Limpar

  1. Feche a segunda guia.
  2. Volte para a primeira guia do Cloud Shell.
  3. 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
  1. Limpe a infraestrutura com o comando a seguir e digite yes para confirmar.
terraform destroy --auto-approve
  1. Verifique se todos os recursos foram encerrados corretamente no projeto ativo:
gcloud container clusters list --region=us-west4
gcloud compute networks list
  1. Exclua o diretório gke-cnp-lab e 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.