Saltar al contenido principal

Traefik

Introducción

En la actualidad, todas las páginas web publicadas, ya sea en Internet o en una intranet, deberían funcionar con HTTPS.

Decirlo y hacerlo es bastante diferente, ya que requiere añadir los certificados en cada servicio, debido a que cada uno lo hace de una forma diferente puede ser un dolor de cabeza. Y mejor no pensar en el día en que caducan y debes cambiarlo en todos los sitios.

Para simplificar todo este proceso y evitar colisión de puertos, ningún servicio expone una web hacia fuera de la red privada del clúster, únicamente un servicio expone por los puertos 80 y 443: el proxy.

La función del proxy es publicar diferentes nombres de dominio, uno para cada servicio del clúster, y a todos incluirle el certificado correspondiente, además, cualquier petición al puerto 80 (HTTP) será redireccionada al 443 (HTTPS).

Internamente, los servicios dentro del clúster se comunican por las redes privadas (y encriptadas) por HTTP, lo que reduce los tiempos de carga y la configuración. Un claro ejemplo es Java, que suele negarse a conectar por HTTPS si desconoce el certificado del otro lado.

En este caso, el proxy es Traefik, un servicio que únicamente se encarga de redireccionar y gestionar los certificados, lo que lo hace más pequeño que Nginx o Apache, los siempre líderes en publicación y proxy.

Descarga

El fichero de despliegue y los ficheros de configuración se encuentran en Git.

git clone https://gitlab.com/ReiIzumi/swarm-project.git
cd swarm-project/00-Proxy

Preparación

Para Traefik vamos a necesitar preparar 2 cosas:

  • Certificados
  • Elegir qué nodos del clúster se encargarán de cada parte (extranet/intranet)

Certificados

Necesitamos 2 tipos de certificados: uno válido en Internet y otro autofirmado para la intranet.

Para Internet tenemos 2 opciones: comprarlo o utilizar Let's Encrypt. Traefik está diseñado para utilizar Let's Encrypt, aunque para ello requiere un volumen compartido donde almacenar los certificados.

En este artículo explicaré únicamente la opción con certificado comprado (que es la misma de autofirmado), más concretamente con el uso de wildcard *.domain.cat. Este permite publicar cualquier subdominio dentro de un dominio (cuidado porque solo funciona a 1 nivel, se acepta web.domain.cat pero no main.web.domain.cat), así que únicamente con 1 certificado será suficiente para publicar todos los servicios.

Sobre los certificados comprados, lo primero a tener en cuenta es que se podrían clasificar en 2 categorías: empresarial y no-empresarial.

Todos los certificados tienen un seguro, si ocurre una incidencia debido al certificado, se puede utilizar ese seguro, pero a no ser que seamos una tienda o gran empresa, no lo vamos a utilizar nunca, con lo que es preferible para nuestro bolsillo optar por los más baratos, este es un ejemplo.

Para los certificados autofirmados, en mi blog se encuentra la explicación de cómo generarlos.

Si se opta por Let's Encrypt, habrá que seguir la documentación y crear un volumen compartido para almacenar estos certificados. Este artículo no usa volúmenes compartidos, pero se puede replicar de la explicación en Portainer.

Extranet/Intranet

Por defecto, un servicio en Docker Swarm publica su puerto en todos los nodos, en este caso tenemos 2 proxys, uno para la extranet y otro para la intranet, así que sus puertos se asociarán al host, esto significa que cada proxy solo será accesible por la IP del host donde esté desplegado.

Debido a que no tenemos un load-balancer y que un router normal únicamente puede publicar 1 IP hacia Internet, lo más lógico será asignar 1 nodo a la extranet y 2 a la intranet:

docker node update --label-add intranet=true Swarm1
docker node update --label-add intranet=true Swarm2
docker node update --label-add extranet=true Swarm3

Novedades del despliegue

Cada despliegue aporta más servicios y puede tener integraciones para otros. En cada sección explicaré las novedades que aporta.

Para publicar un servicio por Traefik, el servicio debe estar en su misma red y tener los labels correctos, no es necesario reconfigurar o reiniciar Traefik para ello.

El servicio debe tener estos labels:

traefik.enable: "true"
traefik.intranet: "true"
traefik.http.services.serviceName.loadbalancer.server.port: "9000"
traefik.http.routers.serviceName.rule: "Host(`service.domain.intranet`)"
traefik.http.routers.serviceName.entrypoints: "websecure"
traefik.http.routers.serviceName.tls: "true"

En cada servicio se deben hacer algunos ajustes:

  • Cada Traefik espera su propio label, traefik.intranet o traefik.extranet, esto evita que un servicio se publique en un proxy inesperado si por algún motivo el servicio tuviera acceso a ambas redes.
  • Se debe indicar el puerto a ser publicado, Traefik no puede detectarlo en un Docker Swarm.
  • El Host debe ser ajustado para cada servicio y debe existir en el DNS.
  • El serviceName debe ser único, si estuviera duplicado, uno de los servicios no sería publicado.

Si una web no tiene autenticación y necesitamos que no sea accesible por cualquiera, podemos ajustar un usuario de acceso:

traefik.http.routers.routerServiceName.middlewares: "serviceName"
traefik.http.middlewares.routerServiceName.basicauth.users: "admin:$$apr1$$75iarhWs$$3nzJ66HnR/2foREExlE/Y1"

Para este caso:

  • El routerServiceName debe ser único y podemos tener varios para el mismo servicio.
  • Cada routerServiceName se ajusta a un serviceName.
  • Hay que ajustar el nombre de usuario y contraseña.

Para construir la contraseña, podemos utilizar el siguiente comando y cambiar los $ por $$:

openssl passwd -apr1 <your-password>

Despliegue

Estos ficheros no disponen de variables, así que deben ser modificados previamente para ser utilizados.

  1. Asignar las labels de intranet y extranet a los nodos. Explicado aquí.
  2. Copiar los ficheros de proxy.yml, traefik-config-intranet.yaml, traefik-config-extranet.yaml y los certificados a un manager.
  3. Actualizar los secrets del proxy.yml acorde al nombre de los certificados. Si se modifica el identificador, también habrá que actualizarlo en cada servicio y los ficheros de configuración de Traefik.
  4. Cambiar el dominio para ambos Traefik: traefik.http.routers.proxy-dashboard.rule. Recordad que el de extranet publicará su página en el dominio de intranet, ya que no queremos publicar esa información hacia fuera.
  5. Actualizar la contraseña de ambos Traefik: traefik.http.middlewares.proxy-dashboard-auth.basicauth.users.
  6. El dashboard del Traefik para extranet no debería estar publicado a internet, así que tiene el dominio hacia la intranet. Para aumentar la seguridad se asigna un rango de IPs al que será aceptado: traefik.http.middlewares.proxy-dashboard-ipwhitelist.ipwhitelist.sourcerange.
  7. Desplegar el stack: docker stack deploy -c proxy.yml proxy.

Después de un tiempo, ambos dashboards deberían ser publicados. Estos nos muestran los datos que han detectado, también es importante revisar que los certificados que están utilizando son los correctos.