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
| Caracteristica | NetworkPolicy (nativa) | CiliumNetworkPolicy |
|---|---|---|
| API | networking.k8s.io/v1 | cilium.io/v2 |
| CNI | Qualquer que implemente | Apenas Cilium |
| Camada | L3/L4 | L3/L4 + L7 |
| Selecao por labels | Sim | Sim |
| Selecao por namespace | Sim | Sim |
| Filtragem por CIDR | Sim | Sim |
| Filtragem por FQDN | Nao | Sim (toFQDNs) |
| Filtragem HTTP (path, method) | Nao | Sim |
| Filtragem DNS | Nao | Sim |
| Filtragem TLS/SNI | Nao | Sim |
| Identity-based | Nao | Sim |
| Portabilidade entre CNIs | Sim | Nao |
Quando usar qual
| Cenario | Escolha |
|---|---|
| Cluster sem Cilium como CNI | NetworkPolicy |
| Precisa filtrar por dominio (FQDN) | CiliumNetworkPolicy |
| Precisa filtrar por HTTP path/method | CiliumNetworkPolicy |
| Quer portabilidade entre diferentes CNIs | NetworkPolicy |
| Precisa de filtragem TLS/SNI | CiliumNetworkPolicy |
| Regras simples de L3/L4 sao suficientes | NetworkPolicy |
| Precisa controlar quais dominios um pod pode resolver via DNS | CiliumNetworkPolicy |
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.