Controla el tráfico de Kubernetes con políticas de red

KubernetesBeginner
Practicar Ahora

Introducción

Los namespaces organizan los recursos, pero no bloquean automáticamente el tráfico de red. Las NetworkPolicies definen qué Pods seleccionados pueden enviar o recibir conexiones, siempre que el complemento de red del clúster las aplique. En este laboratorio, la red Calico ya está preparada para hacerlo.

Comprobarás que al principio el tráfico está abierto, aplicarás una denegación predeterminada y, finalmente, permitirás únicamente a un cliente etiquetado procedente de un namespace aprobado.

El laboratorio sigue un flujo de trabajo seguro para las políticas: establecer una línea base funcional, denegar el tráfico, añadir la excepción mínima necesaria y probar tanto un caso permitido como uno bloqueado. Probar únicamente el cliente permitido podría ocultar una política demasiado amplia.

Implementa el servidor y los clientes

En este paso, crearás namespaces independientes para la aplicación y los clientes, un servidor NGINX y dos clientes de larga duración.

Los comandos utilizan deliberadamente formas imperativas conocidas para que el experimento de red no quede oculto entre YAML no relacionado. Un cliente recibe la etiqueta role=approved y el otro, role=untrusted. El namespace de los clientes recibe access=approved. Más adelante, los selectores de las políticas utilizarán ambos niveles de etiquetas.

cd /home/labex/project/network-policy
kubectl create namespace practice-apps
kubectl create namespace practice-clients
kubectl label namespace practice-clients access=approved

Crea y expón el servidor. kubectl expose crea un Service de tipo ClusterIP cuyo selector coincide con la etiqueta de Pod app=protected-web generada para el Deployment:

kubectl create deployment protected-web -n practice-apps --image=nginx:1.27-alpine --replicas=1
kubectl expose deployment protected-web -n practice-apps --port=80 --target-port=80

Crea dos Pods cliente de diagnóstico. Sus bucles de espera los mantienen disponibles para repetir las pruebas con kubectl exec:

kubectl run crea directamente cada Pod independiente. --labels asigna la identidad que seleccionará la política, --restart=Never evita que kubectl cree un controlador de cargas de trabajo y -- separa las opciones de kubectl del comando sh -c que se ejecuta dentro del contenedor. En el comando de espera, --all indica que todos los Pods de practice-clients deben estar listos.

kubectl run approved-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=approved --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl run untrusted-client -n practice-clients --image=busybox:1.36 --image-pull-policy=IfNotPresent --labels=role=untrusted --restart=Never -- sh -c 'while true; do sleep 30; done'
kubectl wait --for=condition=Ready pod -n practice-clients --all --timeout=120s
kubectl rollout status deployment/protected-web -n practice-apps --timeout=60s

La separación entre namespaces, por sí sola, sigue permitiendo que ambos clientes lleguen al Service.

Los comandos de comprobación de disponibilidad deberían finalizar mostrando evidencias de los dos Pods cliente y del Deployment del servidor:

pod/approved-client condition met
pod/untrusted-client condition met
deployment "protected-web" successfully rolled out

En este punto, las etiquetas describen a los clientes, pero todavía no los restringen. Una etiqueta se convierte en una regla de tráfico únicamente cuando un selector de NetworkPolicy la utiliza.

Comprueba que el tráfico está abierto inicialmente

En este paso, solicitarás el Service entre namespaces desde ambos clientes. El nombre completo incluye el nombre del Service y el namespace.

Dentro del mismo namespace, bastaría con protected-web. Desde practice-clients, protected-web.practice-apps proporciona al DNS del clúster tanto el nombre del Service como su namespace. Prueba ambos clientes antes de aplicar la política para que cualquier fallo posterior pueda atribuirse a la política y no a la configuración del DNS o del Service.

Al igual que en el laboratorio de comprobaciones de salud, wget -qO- escribe el cuerpo de la respuesta sin mostrar información de progreso. --timeout=3 evita que una solicitud bloqueada quede esperando indefinidamente, y la tubería hacia grep muestra únicamente el texto reconocible de NGINX.

cd /home/labex/project/network-policy
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl get networkpolicies -n practice-apps

Cuando no existe ninguna NetworkPolicy que seleccione los Pods, la entrada está permitida. Esto corrige la idea equivocada, bastante común, de que los namespaces son límites de seguridad de red por sí mismos.

Ambos comandos deberían encontrar Welcome to nginx, y kubectl get networkpolicies debería indicar que no hay recursos. Esta es la línea base correcta que cambiarás deliberadamente.

<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
No resources found in practice-apps namespace.

La repetición del texto de la página demuestra que ambas identidades de cliente pueden llegar al mismo Service antes de iniciar el aislamiento.

Aplica la denegación predeterminada de entrada

En este paso, seleccionarás todos los Pods de practice-apps y no permitirás ninguna fuente de entrada.

Una NetworkPolicy solo aplica restricciones a los Pods seleccionados por ella y para la dirección de tráfico indicada en policyTypes. Aquí, podSelector: {} selecciona todos los Pods de practice-apps, y la ausencia de cualquier regla de autorización de ingress crea una denegación predeterminada de la entrada.

cd /home/labex/project/network-policy
cat <<'EOF' > default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: default-deny-ingress, namespace: practice-apps}
spec:
  podSelector: {}
  policyTypes: [Ingress]
EOF
kubectl apply -f default-deny.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'blocked as expected'

Un podSelector vacío selecciona todos los Pods del namespace de la política. El tiempo de espera esperado demuestra que la entrada está denegada; no significa que el Service haya desaparecido.

|| echo ... gestiona el código de salida distinto de cero que wget devuelve como consecuencia esperada, de modo que el terminal pueda continuar. En este experimento controlado, el fallo constituye una evidencia. El Pod del servidor sigue estando listo; lo único que ha cambiado es la admisión de red.

networkpolicy.networking.k8s.io/default-deny-ingress created
wget: download timed out
command terminated with exit code 1
blocked as expected

El tiempo de espera es normal porque un paquete denegado no recibe ninguna respuesta de la aplicación. La última línea confirma que el shell convirtió deliberadamente este fallo esperado en un resultado legible.

Permite un único cliente aprobado

En este paso, añadirás una política de autorización que exige tanto un namespace aprobado como la etiqueta aprobada del Pod.

Las NetworkPolicies se combinan de forma aditiva. La política de denegación predeterminada permanece activa, mientras que esta segunda política añade una única fuente permitida. El Pod de servidor seleccionado solo podrá aceptar tráfico TCP en el puerto 80 cuando coincidan tanto el namespace de origen como el Pod de origen.

cd /home/labex/project/network-policy
cat <<'EOF' > allow-approved.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: {name: allow-approved-client, namespace: practice-apps}
spec:
  podSelector: {matchLabels: {app: protected-web}}
  policyTypes: [Ingress]
  ingress:
    - from:
        - namespaceSelector: {matchLabels: {access: approved}}
          podSelector: {matchLabels: {role: approved}}
      ports:
        - {protocol: TCP, port: 80}
EOF
kubectl apply -f allow-approved.yaml
kubectl exec approved-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps | grep 'Welcome to nginx'
kubectl exec untrusted-client -n practice-clients -- wget -qO- --timeout=3 http://protected-web.practice-apps || echo 'untrusted client blocked'

Como los dos selectores están dentro de la misma entrada from, ambos deben coincidir. Si estuvieran en entradas separadas, representarían una condición lógica OR.

La solicitud del cliente aprobado debería volver a funcionar, mientras que la del cliente no confiable debería seguir agotando el tiempo de espera. Este resultado combinado demuestra que la política es selectiva y no que simplemente haya reabierto toda la entrada.

networkpolicy.networking.k8s.io/allow-approved-client created
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
wget: download timed out
command terminated with exit code 1
untrusted client blocked

Interpreta el resultado como un par: contenido de la página para approved-client y, después, un tiempo de espera para untrusted-client. Ver únicamente la solicitud exitosa no demostraría que la regla es lo bastante específica.

Resumen

Has comprobado que los namespaces no aíslan el tráfico de forma predeterminada, has aplicado una denegación predeterminada de la entrada y has permitido el acceso de un único cliente mediante criterios combinados de namespace, Pod, protocolo y puerto.