Por que Network Policies importam

No Kubernetes, por padrao, todos os pods podem se comunicar livremente entre si, independentemente do namespace. Essa permissividade facilita o desenvolvimento, mas em producao e um problema serio. Um pod comprometido pode escanear a rede interna, acessar bancos de dados, servicos de cache e qualquer outro workload sem restricao.

O modelo Zero Trust parte do principio de que nenhum trafego deve ser confiavel por padrao. Cada comunicacao precisa ser explicitamente autorizada. Network Policies sao o mecanismo do Kubernetes para implementar esse principio na camada de rede, criando segmentacao entre workloads.

Na pratica, isso significa:

  • Pods so acessam o que precisam (principio do menor privilegio)
  • Um servico comprometido nao consegue se mover lateralmente pelo cluster
  • Trafego de saida e controlado, impedindo exfiltracao de dados para destinos nao autorizados

Para que as Network Policies funcionem, o cluster precisa de um CNI (Container Network Interface) que as implemente. Nem todo CNI suporta Network Policies. Entre os que suportam estao Calico, Cilium e Weave Net.


NetworkPolicy nativa do Kubernetes

A NetworkPolicy nativa usa a API networking.k8s.io/v1 e funciona com qualquer CNI que a implemente. Ela opera nas camadas L3/L4 do modelo OSI, ou seja, filtra trafego por IP, porta e protocolo (TCP/UDP).

O que ela suporta

  • Selecao de pods por labels (podSelector)
  • Filtragem por namespace (namespaceSelector)
  • Filtragem por blocos de IP (CIDR)
  • Controle de ingress (trafego de entrada) e egress (trafego de saida)
  • Portas e protocolos especificos

O que ela nao suporta

  • Filtragem por dominio (FQDN)
  • Inspecao de camada 7 (HTTP path, method, headers)
  • Regras baseadas em identidade do servico
  • Filtragem TLS/SNI

Exemplo: default deny para todo o namespace

O primeiro passo em qualquer estrategia de Network Policy e bloquear todo o trafego por padrao. Depois, voce libera apenas o necessario.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: producao
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Com essa policy aplicada, nenhum pod no namespace producao aceita trafego de entrada nem consegue enviar trafego de saida. A partir daqui, voce cria policies especificas para cada fluxo permitido.

Exemplo: permitir ingress de um pod especifico

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-db
  namespace: producao
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api-server
      ports:
        - port: 5432
          protocol: TCP

Apenas pods com o label app: api-server podem acessar a porta 5432 dos pods com label app: postgres. Todo o restante do trafego de ingress e bloqueado.

Limitacoes

A grande limitacao da NetworkPolicy nativa e que ela opera apenas em L3/L4. Voce consegue dizer “pod A pode acessar pod B na porta 8080”, mas nao consegue dizer “pod A pode fazer GET em /api/health mas nao POST em /api/admin”. Alem disso, nao e possivel criar regras baseadas em dominio (FQDN), o que dificulta o controle de acesso a servicos externos cujos IPs mudam com frequencia.


CiliumNetworkPolicy

A CiliumNetworkPolicy usa a API cilium.io/v2 e exige que o Cilium seja o CNI do cluster. Ela oferece tudo que a NetworkPolicy nativa faz, mais capacidades significativas nas camadas superiores.

Capacidades extras

  • Filtragem L7: inspecao de requisicoes HTTP por path, method e headers
  • Regras por FQDN (toFQDNs): permite ou bloqueia acesso a dominios externos
  • DNS-aware: resolve nomes e aplica regras dinamicamente conforme os IPs mudam
  • Filtragem TLS/SNI: controle baseado no Server Name Indication
  • Identity-based: regras baseadas na identidade criptografica dos workloads, nao apenas em labels

Exemplo: filtragem L7 por HTTP path e method

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-health-check-only
  namespace: producao
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: monitoring
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/health"
              - method: GET
                path: "/metrics"

Nesse exemplo, o servico de monitoramento so pode fazer GET em /health e /metrics na porta 8080 do api-server. Qualquer outra requisicao HTTP e bloqueada, mesmo vindo do mesmo pod.

Exemplo: egress restrito por FQDN

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-external-apis
  namespace: producao
spec:
  endpointSelector:
    matchLabels:
      app: payment-service
  egress:
    - toFQDNs:
        - matchName: "api.stripe.com"
        - matchName: "api.pagar.me"
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            "k8s:io.kubernetes.pod.namespace": kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
          rules:
            dns:
              - matchPattern: "*.stripe.com"
              - matchPattern: "*.pagar.me"

O payment-service so pode fazer requisicoes externas para api.stripe.com e api.pagar.me na porta 443. A segunda regra libera consultas DNS ao kube-dns, mas apenas para os dominios permitidos. Sem essa regra de DNS, o pod nao consegue resolver os nomes e a regra de FQDN nao funciona.


Tabela comparativa

CaracteristicaNetworkPolicy (nativa)CiliumNetworkPolicy
APInetworking.k8s.io/v1cilium.io/v2
CNIQualquer que implementeApenas Cilium
CamadaL3/L4L3/L4 + L7
Selecao por labelsSimSim
Selecao por namespaceSimSim
Filtragem por CIDRSimSim
Filtragem por FQDNNaoSim (toFQDNs)
Filtragem HTTP (path, method)NaoSim
Filtragem DNSNaoSim
Filtragem TLS/SNINaoSim
Identity-basedNaoSim
Portabilidade entre CNIsSimNao

Quando usar qual

CenarioEscolha
Cluster sem Cilium como CNINetworkPolicy
Precisa filtrar por dominio (FQDN)CiliumNetworkPolicy
Precisa filtrar por HTTP path/methodCiliumNetworkPolicy
Quer portabilidade entre diferentes CNIsNetworkPolicy
Precisa de filtragem TLS/SNICiliumNetworkPolicy
Regras simples de L3/L4 sao suficientesNetworkPolicy
Precisa controlar quais dominios um pod pode resolver via DNSCiliumNetworkPolicy

Se o cluster ja usa Cilium, aproveite as CiliumNetworkPolicies para cenarios que exigem mais granularidade. Para regras basicas de isolamento entre pods e namespaces, a NetworkPolicy nativa e suficiente e mais portavel.

As duas abordagens nao sao mutuamente exclusivas. E possivel usar NetworkPolicy nativa para regras L3/L4 basicas e CiliumNetworkPolicy para regras L7 e FQDN no mesmo cluster com Cilium.


Exemplo pratico: liberando acesso a APIs externas por dominio

Um cenario comum e quando um servico precisa acessar APIs externas como gateways de pagamento, servicos de monitoramento ou APIs de terceiros. Com a NetworkPolicy nativa, voce precisaria definir blocos CIDR para cada IP desses servicos. O problema e que esses IPs mudam com frequencia (CDNs, load balancers, failovers), e manter CIDRs fixos se torna inviavel.

Com a CiliumNetworkPolicy e toFQDNs, voce define o dominio diretamente. O Cilium intercepta as resolucoes DNS e atualiza as regras de firewall automaticamente conforme os IPs mudam.

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: notification-service-egress
  namespace: producao
spec:
  endpointSelector:
    matchLabels:
      app: notification-service
  egress:
    - toFQDNs:
        - matchName: "api.sendgrid.com"
        - matchName: "fcm.googleapis.com"
        - matchName: "api.twilio.com"
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            "k8s:io.kubernetes.pod.namespace": kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
          rules:
            dns:
              - matchPattern: "*.sendgrid.com"
              - matchPattern: "*.googleapis.com"
              - matchPattern: "*.twilio.com"

O notification-service so pode enviar requisicoes para SendGrid, Firebase Cloud Messaging e Twilio. Qualquer tentativa de acessar outro dominio ou IP e bloqueada. Se um atacante comprometer esse pod, ele nao consegue exfiltrar dados para servidores externos.


Comunicacao pod-a-pod via Service DNS

Para comunicacao interna entre servicos no cluster, tanto a NetworkPolicy nativa quanto a CiliumNetworkPolicy permitem selecionar pods por labels. No caso do Cilium, a diretiva toEndpoints funciona de forma similar ao podSelector, mas integrada ao modelo de identidade do Cilium.

Os pods se comunicam usando o DNS interno do Kubernetes:

  • Mesmo namespace: http://nome-do-service:porta
  • Namespace diferente: http://nome-do-service.namespace.svc.cluster.local:porta

Exemplo: api-server acessa postgres e redis no mesmo namespace

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-server-egress
  namespace: producao
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  egress:
    - toEndpoints:
        - matchLabels:
            app: postgres
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            app: redis
      toPorts:
        - ports:
            - port: "6379"
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            "k8s:io.kubernetes.pod.namespace": kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP

O api-server so pode se comunicar com postgres na porta 5432 e redis na porta 6379. A regra de DNS permite que ele resolva os nomes dos services internos. A Network Policy controla se a comunicacao e permitida. O DNS do Service resolve para onde o trafego vai. Sao camadas complementares.


Referencias