NetworkPolicy permite controlar el tráfico de entrada y salida de los Pods mediante selectores de etiquetas, protocolos, puertos y namespaces.
Estas políticas solo se aplican si el plugin de red del clúster implementa la API. Kubernetes puede aceptar un manifiesto válido aunque el CNI no haga cumplir sus reglas. Por eso, la compatibilidad debe confirmarse con la documentación del proveedor y mediante pruebas reales de conectividad.
El objetivo recomendado es partir de una postura restrictiva y habilitar únicamente los flujos necesarios.

¿Cuándo conviene utilizar NetworkPolicy?
Resulta especialmente útil en:
- Clústeres con múltiples aplicaciones o equipos.
- Entornos que procesan información sensible.
- Namespaces compartidos.
- Arquitecturas de microservicios.
- Aplicaciones que solo necesitan comunicarse con dependencias concretas.
- Entornos donde debe limitarse la salida hacia Internet.
NetworkPolicy opera principalmente en las capas de red y transporte. No reemplaza la autenticación de las aplicaciones, TLS, RBAC ni la autorización de una base de datos.
Cómo se evalúan las políticas
Las políticas de Kubernetes son acumulativas. No existe una prioridad entre ellas ni una regla estándar de denegación explícita: los permisos efectivos son la unión de todas las políticas aplicables.
Para que una conexión entre dos Pods aislados funcione, deben permitirse ambos extremos:
- El egress del Pod de origen.
- El ingress del Pod de destino.
Si cualquiera de los dos lados no permite la conexión, el tráfico queda bloqueado. El tráfico de respuesta de una conexión autorizada se permite implícitamente. Documentación oficial de NetworkPolicy.
En este ejemplo:
- La API podrá consultar DNS.
- La API podrá conectarse con PostgreSQL mediante TCP 5432.
- PostgreSQL aceptará conexiones únicamente desde Pods de la API.
- Un Pod sin las etiquetas autorizadas no podrá alcanzar PostgreSQL.
- Cualquier otro flujo quedará bloqueado al activar las políticas por defecto.
Requisitos previos
Antes de comenzar, verifica que dispones de:
- Un clúster Kubernetes operativo.
- Un CNI compatible con
NetworkPolicy. kubectlconfigurado.- Permisos para administrar políticas en el namespace.
- Un namespace llamado
app-ns. - Deployments llamados
apiypostgres. - Un Service llamado
postgresque publique TCP 5432. - Etiquetas estables en las plantillas de Pods.
Comprueba que el recurso esté disponible:
kubectl api-resources | grep -i networkpolicy
kubectl auth can-i create networkpolicies.networking.k8s.io -n app-nsQue la API exista no demuestra que el CNI aplique las reglas. Consulta la implementación instalada:
kubectl get daemonset -n kube-system
kubectl get pods -n kube-system -o wideConfirma el soporte en la documentación específica del CNI y realiza las pruebas positiva y negativa incluidas al final.
Paso 1. Inventariar los flujos
Antes de bloquear tráfico, documenta como mínimo:
- Qué Pods reciben conexiones.
- Qué Pods inician conexiones.
- Puertos y protocolos utilizados.
- Acceso al DNS del clúster.
- Acceso a métricas, logs, proxies o service meshes.
- Dependencias externas, APIs y repositorios.
- Tráfico del Ingress Controller o Gateway hacia la API.
- Tareas de backup, migraciones y monitorización.
El ejemplo solo habilita DNS y API → PostgreSQL. Si la API recibe tráfico desde un Gateway o necesita conectarse con otros servicios, esas reglas deben declararse antes de activar la denegación por defecto.
Paso 2. Etiquetar las plantillas de Pods
NetworkPolicy selecciona Pods, no Deployments. Ejecutar lo siguiente:
kubectl label deployment api app=apisolo modifica las etiquetas del objeto Deployment; no es una forma fiable de etiquetar los Pods administrados por su plantilla.
Define las etiquetas en spec.template.metadata.labels del manifiesto de cada workload:
# Fragmento del Deployment api
spec:
template:
metadata:
labels:
app.kubernetes.io/name: api
app.kubernetes.io/component: backend# Fragmento del Deployment postgres
spec:
template:
metadata:
labels:
app.kubernetes.io/name: postgres
app.kubernetes.io/component: databaseNo cambies arbitrariamente spec.selector, porque en un Deployment existente es un campo sensible y normalmente inmutable. Conserva también las etiquetas utilizadas por los Services.
Modificar la plantilla provoca un rollout. Comprueba su finalización:
kubectl rollout status deployment/api -n app-ns
kubectl rollout status deployment/postgres -n app-ns
kubectl get pods -n app-ns \
-L app.kubernetes.io/name,app.kubernetes.io/componentLas etiquetas recomendadas bajo app.kubernetes.io/ facilitan la interoperabilidad con herramientas y automatizaciones. Convenciones oficiales de etiquetas.
Paso 3. Identificar el servicio DNS
Antes de crear la excepción, comprueba cómo está desplegado DNS:
kubectl get service -n kube-system kube-dns
kubectl get pods -n kube-system \
-l k8s-app=kube-dns --show-labelsMuchas instalaciones de CoreDNS utilizan k8s-app=kube-dns, pero no es una garantía universal. Si la consulta no devuelve Pods, inspecciona las etiquetas reales:
kubectl get pods -n kube-system --show-labelsLos clústeres con NodeLocal DNSCache u otra arquitectura pueden requerir una regla diferente, basada en la documentación del CNI y en la dirección DNS configurada en los Pods.
Paso 4. Permitir el acceso a DNS
Crea allow-dns.yaml, ajustando el selector si tu instalación utiliza otras etiquetas:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: app-ns
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Se permiten UDP y TCP. Aunque la mayoría de las consultas utiliza UDP, los clientes pueden recurrir a TCP, por ejemplo ante respuestas truncadas.
namespaceSelector y podSelector aparecen dentro del mismo elemento de to. Por lo tanto, se evalúan conjuntamente: deben ser Pods con la etiqueta indicada dentro de kube-system.
Paso 5. Autorizar API → PostgreSQL
Se necesitan dos políticas: una permite la salida desde la API y otra la entrada en PostgreSQL.
Crea allow-api-postgres.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-egress-postgres
namespace: app-ns
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: postgres
ports:
- protocol: TCP
port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-postgres-ingress-api
namespace: app-ns
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: api
ports:
- protocol: TCP
port: 5432Como no hay un namespaceSelector, estos selectores solo coinciden con Pods del mismo namespace que la política.
Si la API y PostgreSQL estuvieran en namespaces diferentes, habría que combinar namespaceSelector y podSelector en un mismo elemento. Separarlos como dos elementos distintos produciría una condición OR y podría autorizar más tráfico del esperado.
Paso 6. Preparar la denegación por defecto
Crea default-deny-ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app-ns
spec:
podSelector: {}
policyTypes:
- IngressCrea default-deny-egress.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: app-ns
spec:
podSelector: {}
policyTypes:
- EgressUn podSelector: {} selecciona todos los Pods del namespace. La ausencia de reglas ingress o egress significa que esas políticas no agregan ningún permiso.
La documentación oficial incluye estas políticas como patrones de denegación por defecto. Guía oficial para declarar NetworkPolicy.
Paso 7. Validar los manifiestos
Antes de aplicar cambios:
kubectl apply --dry-run=server -f allow-dns.yaml
kubectl apply --dry-run=server -f allow-api-postgres.yaml
kubectl apply --dry-run=server -f default-deny-ingress.yaml
kubectl apply --dry-run=server -f default-deny-egress.yamlRevisa también las diferencias:
kubectl diff -f allow-dns.yaml
kubectl diff -f allow-api-postgres.yaml
kubectl diff -f default-deny-ingress.yaml
kubectl diff -f default-deny-egress.yamlLa validación del API server confirma la estructura del recurso, pero no puede demostrar que los selectores correspondan a los Pods correctos ni que el CNI aplique la política.
Paso 8. Aplicar las políticas por etapas
Aplica primero los permisos:
kubectl apply -f allow-dns.yaml
kubectl apply -f allow-api-postgres.yamlComprueba que seleccionen los Pods esperados:
kubectl describe networkpolicy allow-dns -n app-ns
kubectl describe networkpolicy allow-api-egress-postgres -n app-ns
kubectl describe networkpolicy allow-postgres-ingress-api -n app-nsDespués activa la restricción de ingress:
kubectl apply -f default-deny-ingress.yamlValida la aplicación. Si todo funciona, activa egress:
kubectl apply -f default-deny-egress.yamlEste orden reduce el riesgo de cerrar una dependencia antes de declarar su excepción.
Ver la Imagen 2: despliegue seguro por etapas.
Paso 9. Probar DNS
Ejecuta un Pod temporal sin permisos especiales:
kubectl run netcheck-dns \
--rm -i \
--restart=Never \
--namespace=app-ns \
--labels="app.kubernetes.io/name=netcheck" \
--image=busybox:1.36 \
-- nslookup kubernetes.default.svc.cluster.localLa resolución debe funcionar gracias a allow-dns. Si falla, revisa el Service DNS, las etiquetas de los Pods y si el clúster utiliza NodeLocal DNSCache.
Paso 10. Probar una conexión autorizada
El siguiente Pod temporal recibe la etiqueta autorizada para simular el origen de la API:
kubectl run netcheck-api \
--rm -i \
--restart=Never \
--namespace=app-ns \
--labels="app.kubernetes.io/name=api" \
--image=postgres:16-alpine \
-- pg_isready -h postgres -p 5432 -t 3El resultado esperado es:
postgres:5432 - accepting connectionsEsta prueba confirma DNS, el Service y el puerto. También demuestra una consideración de seguridad importante: las etiquetas no representan identidades criptográficas. Cualquier usuario con permiso para crear Pods o modificar sus etiquetas podría intentar adoptar app.kubernetes.io/name=api; limita esas capacidades mediante RBAC.
Paso 11. Probar una conexión bloqueada
Ejecuta la misma prueba sin la etiqueta autorizada:
kubectl run netcheck-unauthorized \
--rm -i \
--restart=Never \
--namespace=app-ns \
--labels="app.kubernetes.io/name=netcheck" \
--image=postgres:16-alpine \
-- pg_isready -h postgres -p 5432 -t 3La conexión debería agotar el tiempo de espera o informar que no obtiene respuesta. Un timeout es normal: las implementaciones suelen descartar los paquetes en lugar de rechazar activamente la conexión.
No consideres finalizada la validación hasta confirmar tanto el acceso permitido como el bloqueo no autorizado.
Comprobaciones posteriores
Revisa:
kubectl get networkpolicy -n app-ns
kubectl get pods -n app-ns --show-labels
kubectl get service postgres -n app-ns
kubectl get endpointslice -n app-ns \
-l kubernetes.io/service-name=postgresComprueba además:
- Readiness y logs de la API.
- Conexiones reales hacia PostgreSQL.
- Resolución DNS.
- Métricas, monitorización y recolección de logs.
- Acceso desde Gateways o controladores de Ingress.
- Dependencias externas necesarias.
- Pruebas negativas desde Pods no autorizados.
La API estándar no define logs de paquetes descartados. Para observarlos se necesitan las capacidades de diagnóstico del CNI o de la plataforma.
Reversión
Si la política de egress interrumpe una dependencia no inventariada, elimina primero la restricción correspondiente:
kubectl delete networkpolicy default-deny-egress -n app-nsSi el problema afecta las conexiones de entrada:
kubectl delete networkpolicy default-deny-ingress -n app-nsDespués revisa todas las políticas restantes. Como son acumulativas, eliminar una política no garantiza que el Pod deje de estar aislado si otra política también lo selecciona.
Problemas frecuentes
La política no bloquea tráfico
Verifica:
- Que el CNI implemente
NetworkPolicy. - Que la política esté en el namespace correcto.
- Que
podSelectorcoincida con las etiquetas de los Pods, no solo con las del Deployment. - Que la prueba utilice una conexión nueva.
- Que no estés interpretando una política adicional como una regla de denegación: las políticas solo agregan permisos.
La aplicación pierde resolución DNS
Comprueba:
- Las etiquetas reales de CoreDNS.
- Los protocolos UDP y TCP en el puerto 53.
- El namespace seleccionado.
- La dirección de
nameserveren/etc/resolv.conf. - Si el clúster utiliza NodeLocal DNSCache.
La API no puede acceder a PostgreSQL
Revisa ambos extremos:
- Egress de los Pods
api. - Ingress de los Pods
postgres. - Puerto del Service.
- EndpointSlices disponibles.
- Etiquetas de los Pods.
- Resolución del nombre
postgres.
La API deja de recibir tráfico externo
default-deny-ingress afecta a todos los Pods de app-ns. Debes crear una política que autorice el origen real del Gateway, Ingress Controller o proxy antes de aplicar la denegación. Las etiquetas y el namespace de ese componente dependen de la instalación.
La aplicación pierde acceso a Internet
Declara las dependencias externas antes de cerrar egress. La API estándar de NetworkPolicy no permite seleccionar destinos por nombre DNS; las políticas basadas en FQDN son extensiones específicas de algunos CNI.
Buenas prácticas para producción
- Mantén una política deny-by-default por namespace.
- Declara permisos antes de activar las restricciones.
- Utiliza etiquetas estables y administradas declarativamente.
- Restringe mediante RBAC quién puede modificar workloads, Pods y etiquetas.
- Valida tanto conexiones permitidas como bloqueadas.
- Incluye DNS, monitorización, backups y componentes de entrada.
- Versiona las políticas junto con la aplicación.
- Audita los flujos después de cada release.
- Documenta el origen, destino, protocolo, puerto y justificación de cada regla.
- Prueba primero en staging con una topología representativa.
Conclusión
NetworkPolicy permite reducir el movimiento lateral dentro del clúster, pero una política válida no es necesariamente una política correcta. Su efectividad depende del CNI, de selectores precisos y de que todas las dependencias hayan sido inventariadas.
