Network Policies
Vamos a iniciar con lo básico de redes...

Las respuestas quedan permitidas para quien envió la solicitud. Necesitamos controlar lo que ella puede recibir y hacia dónde pueden ir.
En este escenario:
La aplicación frontend debe permitir un tráfico de entrada en el puerto 443 HTTPS que ella escucha para Internet y debe permitir la comunicación con el backend en el puerto 5000.
La aplicación backend solamente debe aceptar solicitud en el puerto 5000, responderlas, y puede hacer solicitud para el database en el puerto 3306.
El database solamente acepta solicitudes en el puerto 3306 y responderlas, no aceptando salir para ningún lugar.
Para eso necesitamos crear reglas para los ingress y egress de esas aplicaciones.
Ingress Rule son las reglas de entrada y Egress Rule son las reglas de salida.
¿Cuáles reglas son necesarias para que todo funcione?
-
Frontend
- Permitir puerto 443 en ingress
- Permitir backend:5000 en egress
-
Backend
- Permitir puerto 5000 en ingress
- Permitir database:3306 de egress
-
Database
- Permitir puerto 3306 en ingress
Uno de los prerequisitos para el workflow en kubernetes es que cualquier pod pueda comunicarse con los otros sin ninguna configuración adicional como rutas.

En esta solución de redes todos los pods están es una red virtual que se extiende a través de los nodes en el cluster kubernetes y todos ellos, POR DEFECTO, pueden llegar a los otros a través de ips, nombres y services configurados para este propósito.
Kubernetes es configurado por defecto con una regla (All Allow) que permite el tráfico de cualquier pod para cualquier otro pod.
Volviendo al escenario propuesto, tanto el frontend, backend y database pueden comunicarse entre sí sin ningún impedimento dentro del cluster. Las reglas están yendo más allá de lo que queremos; para eso necesitamos restringir esos pods.
Políticas de red son aplicadas a los pods, no en los pods. Es una regla completamente independiente del pod.
Por ejemplo, necesitamos que el database solamente permita tráfico viniendo de los pods backend en el puerto 3306.
Para vincular una regla de red a un pod, usamos la misma técnica que vinculamos replicasets con pods y services con pods, usando labels y selectors.


apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada en todos los pods que tienen la label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Reglas de entrada acepta el tráfico de pods que tienen la label app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: backend
# En el puerto 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306
Solo existe efecto si tiene ingress y o egress en el policy types pues la regla debe ser aplicada o en la entrada o en la salida.
El CNI usado para solución de network en el cluster debe soportar el network policies, caso contrario no surtirá efecto.
Calico, Cilium, Weavenet, Kube-router soportan nativamente network policies
Flannel no soporta.
Ahora vamos a pensar en lo siguiente. Tenemos diferentes pods que pueden tener el mismo label, pero en diferentes namespaces.

De esa forma podemos también cerrar a nivel de namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada en todos los pods que tienen la label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Reglas de entrada acepta el tráfico de pods que tienen la label app: backend y están en el namespace prod.
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
# En el puerto 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306
Si solamente usáramos el namespaceSelector todos los pods dentro del namespace podrían tener acceso al database.
¿Y si un servidor de backup necesitara acceder directamente a ese database? En la regla anterior no funcionaría.
Pero si analizamos mejor el yaml veremos que from es una lista de reglas que son permitidas. Si alguna de las reglas satisface entonces puede pasar. En el ejemplo arriba solamente tenemos una regla (ser del namespace prod y tener la label backend). Vamos a añadir entonces otra regla.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
# Será aplicada en todos los pods que tienen la label app: database
matchLabels:
app: database
policyTypes:
- Ingress
# Reglas de entrada acepta el tráfico de pods que tienen la label app: backend y están en el namespace prod.
ingress:
- from:
########### 1 regla ##########
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
########### 2 regla ##########
- ipBlock:
cidr: 192.168.5.10/32 # Ip del servidor de backup
# En el puerto 3306 usando protocolo tcp
ports:
- protocol: TCP
port: 3306
Esto muestra que tenemos dos reglas de entrada y cualquiera que sea satisfecha es suficiente para permitir la conexión.
Regla 1 - Estar en el namespace de prod y el pod precisa tener la label app: backend Regla 2 - La conexión precisaría venir de 192.168.5.10
Si hubiera hecho esto, apenas cometido el error de colocar un - en frente de namespaceSelector.
ingress:
- from:
- podSelector:
matchLabels:
app: backend
- namespaceSelector:
matchLabels:
name: prod
- ipBlock:
cidr: 192.168.5.10/32 # Ip del servidor de backup
Tendría 3 reglas diferentes:
Regla 1 - Cualquier pod que tenga la label app: backend no importando el namespace podría ser permitido.
Regla 2 - Cualquier pod en el namespace prod sería permitido
Regla 3 - La solicitud viniendo del ip 192.168.5.10 sería permitida
¡Basta un error aquí que todo puede cambiar!
Pero vamos a imaginar que el database quien conecta con el backup en el puerto 80 y no lo contrario, luego.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Añadido también regla de salida
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
Tanto el from del ingress y el to del egress también son listas. Luego podríamos aún tener más reglas dentro de la misma policy.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Añadido también regla de salida
ingress:
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: prod
ports:
- protocol: TCP
port: 3306
- from:
- podSelector:
matchLabels:
app: backend
namespaceSelector:
matchLabels:
name: staging
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 443
Yo sé que esto podría ser más simple, pero fue solo para ilustrar.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dbpolicy
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress # Añadido también regla de salida
ingress:
- from:
- podSelector:
matchLabels:
app: backend
matchExpressions:
- key: name
operator: In
values:
- prod
- staging
ports:
- protocol: TCP
port: 3306
egress:
- to:
- ipBlock:
cidr: 192.168.5.10/32
ports:
- protocol: TCP
port: 80
ports:
- protocol: TCP
port: 443