Escribe network policies: de reglas L3/L4 a L7
Kubernetes parte de que todo Pod puede alcanzar a cualquier otro Pod; CiliumNetworkPolicy es la forma de acotarlo, hasta el método HTTP si lo necesitas.
De forma predeterminada, el networking de Kubernetes no tiene restricciones: cualquier Pod puede alcanzar a cualquier otro Pod del clúster, en cualquier puerto. Para un clúster con algo de multi-tenancy o cualquier requisito de seguridad más allá de “confiar en todo lo interno”, ese valor predeterminado suele ser incorrecto. CiliumNetworkPolicy es la forma de acotarlo, desde simples listas de permitidos hasta reglas que inspeccionan la petición HTTP real.
El punto de partida: todo está abierto
Confirma el comportamiento predeterminado antes de cambiar nada:
kubectl run client --image=curlimages/curl --command -- sleep infinity
kubectl exec client -- curl -s http://some-other-service.default.svc.cluster.local
Sin políticas, esa petición tiene éxito sin importar qué sea client ni qué haga some-other-service. Una vez que cualquier política selecciona a un Pod para una dirección dada (ingress o egress), el tráfico de ese Pod en esa dirección pasa a ser default-deny: solo pasa lo que la política permite explícitamente. Un Pod al que no selecciona ninguna política sigue totalmente abierto. Esto es lo más importante que hay que entender antes de escribir tu primera política: escribir una demasiado acotada puede cortar en silencio tráfico que no pretendías tocar, para cualquier Pod que seleccione.
L3/L4: restringe por identidad y puerto
La forma más común de política restringe qué Pods pueden alcanzar a qué Pods, y en qué puertos, usando la identidad basada en etiquetas de Cilium (cubierta en la guía de esta serie sobre cómo usa Cilium eBPF) en lugar de direcciones IP:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
Léelo así: se aplica a los Pods con la etiqueta app: api (endpointSelector) y permite tráfico entrante (ingress) solo desde Pods con la etiqueta app: frontend, solo por el puerto TCP 8080. Cualquier otro Pod, y cualquier otro puerto, queda ahora denegado hacia los Pods api, ya que la existencia de esta política los pone en default-deny para ingress.
Egress funciona igual, en la otra dirección
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-egress-to-database
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
egress:
- toEndpoints:
- matchLabels:
app: database
toPorts:
- ports:
- port: "5432"
protocol: TCP
Los Pods api ahora solo pueden iniciar conexiones salientes hacia Pods database por el puerto 5432. Combina reglas de ingress y egress en la misma política, o mantenlas separadas, según lo que resulte más claro en tu entorno; funcionalmente, varias políticas con el mismo endpointSelector sobre los mismos Pods se componen correctamente.
Permitir tráfico hacia o desde fuera del clúster
fromEndpoints/toEndpoints coinciden con Pods por etiqueta. Para el tráfico hacia o desde algo fuera de la red de Pods del clúster, se aplican en su lugar reglas basadas en DNS o en CIDR:
egress:
- toFQDNs:
- matchName: "api.external-service.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
toFQDNs es una extensión específica de Cilium más allá de la NetworkPolicy estándar de Kubernetes, que te permite permitir egress hacia un dominio externo con nombre en lugar de tener que conocer y fijar de antemano sus direcciones IP, algo importante para cualquier servicio externo cuyas IP no controlas y puedan cambiar sin aviso.
L7: política que lee la petición real
Todo lo anterior opera en L3/L4: identidades y puertos, sin mirar dentro del tráfico. Cilium puede ir más lejos y tomar decisiones según el contenido de los protocolos L7 que entiende, siendo HTTP el más común:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-read-only-for-reporting
namespace: default
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: reporting
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/reports/.*"
Esto permite que el servicio reporting alcance a api por el puerto 8080, pero solo con peticiones GET cuya ruta coincida con /reports/.*. Un POST al mismo puerto, o un GET a una ruta no listada, se deniega, aunque sean los mismos Pods en el mismo puerto que las reglas L3/L4 por sí solas habrían permitido en bloque. Esto lo aplica el Envoy embebido de Cilium para los Pods a los que se aplica la regla; la política L7 tiene un costo de rendimiento real pero generalmente modesto en comparación con la aplicación pura L3/L4 en eBPF, algo a tener en cuenta en rutas de muy alto rendimiento donde la restricción L3/L4 es suficiente para tu requisito de seguridad real.
Prueba antes de confiar
Una política mal aplicada falla de forma cerrada, denegando tráfico, no abierta, que es el modo de fallo más seguro pero sigue siendo una interrupción si es el tráfico equivocado. Antes de aplicar una política restrictiva nueva a algo que sirve tráfico real:
kubectl apply --dry-run=server -f policy.yaml
Luego aplícala primero en un entorno inferior, genera tráfico que debería permitirse y tráfico que debería denegarse, y confirma que ambos se comportan como se espera, antes de promover la misma política a cualquier cosa que sirva producción.
Empieza con todo permitido y acota de forma deliberada
Un camino de adopción razonable para un clúster sin políticas existentes: no intentes escribir un conjunto de políticas completo y correcto el primer día. Empieza observando los patrones de tráfico reales (Hubble, cubierto en la página de inicio de esta tecnología, está hecho exactamente para esto), escribe políticas que coincidan con lo que se observa como legítimo, aplícalas primero en un entorno que no sea de producción y solo entonces acota el tráfico de producción para que coincida. Escribir políticas restrictivas basadas en patrones de tráfico supuestos, en lugar de observados, es una forma habitual de causar una interrupción que parece un problema de Cilium pero en realidad es una política que no tuvo en cuenta un patrón legítimo que nadie pensó en listar.
Guías populares
-
Cómo usa Cilium eBPF: identidad, políticas y sockets Qué ocurre realmente en el kernel cuando un Pod arranca, habla con otro Pod o recibe una decisión de política sobre un paquete, y por qué es rápido. -
Prepara un clúster para Cilium: desactiva primero la CNI predeterminada Cilium reemplaza por completo el networking de tu clúster, lo que significa que el orden de las operaciones importa más que con la mayoría de los complementos. -
Expón Services con el soporte de Gateway API de Cilium GatewayClass, Gateway y HTTPRoute: enrutamiento por host, coincidencia de rutas, reescrituras de URL y TLS, sin Pods de gateway separados.