Azure Application Gateway con WAF y wildcard + Nginx Controller para AKS
Hace ya unas cuantas semanas atrás, mis compañeros Carlos Mendible, David Sancho y una servidora estuvimos dándole vueltas a una necesidad de un cliente que requería lo siguiente: la idea era utilizar Azure Application Gateway, por su característica de WAF, como Ingress de Kubernetes. El problema es que a día de hoy, al poner este en modo WAF solo soporta hasta 40 listeners y en este escenario consideraban que se les quedaría pequeño en un futuro cercano. Es por ello que se presentó la siguiente alternativa: usar Application Gateway con la posibilidad de usar wildcard para el listener, de tal manera que pudiéramos tener más de un listener en uno, y que un Ingress Controller, como por ejemplo Nginx, tuviera las reglas necesarias para saber a dónde tenía que mandar la petición.
En este artículo te comparto la prueba de concepto que se desarrolló, por si te fuera de utilidad.
Configuración del clúster
Lo primero que necesito es un clúster de Kubernetes sobre el que montar esta prueba. Para ello puedo utilizar los siguientes comandos, para crear el grupo de recursos y este:
Ahora ya tenemos un clúster limpio donde montar el escenario descrito al inicio.
Instalar Nginx Controller
Lo siguiente que vamos a hacer es desplegar un Ingress con Nginx que será el encargado de enrutar a los servicios correctos cuando llegue una petición desde mi WAF. Para esta prueba vamos a instalar la configuración básica que aparece en la documentación:
Durante la instalación con Helm le he pasado un archivo adicional con la configuración necesaria para que el ingress controller tenga un balanceador privado, no una IP pública:
Una vez que finalice, podemos comprobar que el balanceador tiene la IP correcta:
y ya podemos poner algunas aplicaciones de ejemplo.
Clientes de ejemplo y health probe
Ahora vamos a simular que tenemos varios despliegues en nuestro clúster, que corresponden a diferentes clientes y entornos para estos. Este es un ejemplo de un entorno de dev para client1:
Como ves, creo un conjunto de recursos con estos nombres:
- Namespace: client1-dev
- Deployment: webapp-client1-dev-whoami
- Service: svc-client1-dev-whoami
- Ingress: webapp-client1-dev-ingress
Me he preocupado de que todos los recursos que pertenecen al mismo entorno tenga el identificador del cliente al que corresponde para que, a la hora de validar la prueba, supiera que estaba bien. Hay otra carpeta en el repositorio de GitHub que simula hasta tres clientes más, siguiendo la misma estructura con el nombre de los clientes.
Puedes desplegar estos utilizando el siguiente comando:
Por otro lado, además de los clientes, he creado un despliegue en un namespace diferente para las pruebas de disponibilidad que necesita Application Gateway, para saber que un backend está respondiendo correctamente:
Para desplegarlo, puedes utilizar este comando:
Si quisieras comprobar que este test funciona correctamente puedes hacerlo a través de estos comandos:
Application Gateway
El siguiente paso es crear un Application Gateway en modo WAF dentro de una subnet de la red donde se encuentra este clúster de Kubernetes, por simplicidad en la PoC. El mismo se conectará de manera interna con el ingress controller al que le pasará las peticiones con el hostname con el que le han llegado. Para todo esto puedes utilizar los siguientes comandos:
En mi ejemplo, voy a usar el dominio azuredemo.es al que configuro con un wildcard, de tal forma que pueda usar subdominios con el mismo listener.
Nota: esta funcionalidad todavía está en Preview, por lo que más allá de la prueba de concepto todavía no puede ser usada en un entorno productivo.
Configuración del dominio
Para que todo esto funcione, además es necesario configurar el DNS de tu dominio para que acepte los subdominios que he utilizado para este ejercicio:
La IP que aparece para el registro A es la IP pública de mi Application Gateway.
Cómo probar que funciona
Si ahora compruebo todos los recursos de tipo Ingress que me he generado:
Debería de ver algo como lo siguiente:
Las pruebas que me he generado, para ver que efectivamente todo esto funciona, han sido utilizando la extensión para Visual Studio Code llamada REST Client y las he dejado en este archivo, el cual contiene lo siguiente:
El resultado sería algo como esto:
La última petición debería de fallar porque no hay ningún entorno para ese cliente (así confirmamos que funciona realmente cuando debe).
El código del ejemplo lo tienes en mi GitHub.
¡Saludos!