HAProxy + Let's Encrypt
HAProxy es un balanceador de carga TCP/HTTP de alta performance. Lo conocí en un proyecto donde lo usábamos como proxy reverso hacia las aplicaciones web que manteníamos, lo cual nos resultaba muy cómodo por su fácil configuración.
Me encontré en otro proyecto teniendo que exponer un servicio por HTTPS para una demo. Para ello había que tener un certificado TLS/SSL, lo cual gracias a Let's Encrypt estaba cubierto. Y para evitar que los desarrolladores toquen el código decidí levantar un HAProxy, que éste manejara el certificado y redirigir las peticiones hacia el servicio.
Acá la receta:
Instalar HAProxy
En Ubuntu:
$ sudo apt-get install -y haproxy
Instalar CertBot
CertBot es una herramienta que permite automatizar varias de las tareas de obtener un certificado TLS/SSL de Let's Encrypt.
Además permite levantar un webserver para que Let's Encrypt pueda hacer sus validaciones y emitir el certificado TLS/SSL.
En Ubuntu:
$ sudo add-apt-repository -y ppa:certbot/certbot
$ sudo apt-get update
$ sudo apt-get install -y certbot
Agregar CertBot a la configuracion de HAPRoxy
Asumiendo que se tiene un frontend fe-web
en la configuración del HAProxy escuchando en el puerto 80, agregar en éste una regla acl
para chequear si la URI corresponde con la ruta /.well-known/acme-challenge/
en cuyo caso se envía al backend be-certbot
asociado al webserver de CertBot.
También hay que agregar el backend be-certbot
que configuraremos para servir en el puerto 8888.
# El frontend sólo esucha el puerto 80
# Si detecta una petición de LetsEncrypt entonces utiliza el backend be-certbot
# Si no va al backend por defect be-defaul
frontend fe-web
bind *:80
# Si la URI es una peticion de letsencrypt entonces la envía al backend del certbot
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend be-certbot if letsencrypt-acl
default_backend be-default
# Backend de CertBot
backend be-certbot
server certbot 127.0.0.1:8888
# Backend por defecto
backend be-default
# Acá va la configuracion del backend (se omite)
Reiniciar HAProxy
En Ubuntu:
$ service haproxy reload
Ejecutar CertBot
Asumiendo que nuestro dominio es www.mi-dominio.com
y el correo a utilizar en el certificado es admin@mi-dominio.com
.
$ certbot certonly --standalone -d www.mi-dominio.com --non-interactive --agree-tos --email admin@mi-dominio.com --http-01-port=8888
Crear certificado SSL para HAProxy
HAProxy necesita el certificado de manera particular, para ello necesitamos concatenar el contenido del certificado y su cadena con su clave privada. Dejaremos este certificado en el directorio /etc/ssl/www.mi-dominio.com
. Para ello:
$ sudo mkdir -p /etc/ssl/www.mi-dominio.com
$ sudo cat /etc/letsencrypt/live/www.mi-dominio.com/fullchain.pem \
/etc/letsencrypt/live/www.mi-dominio.com/privkey.pem \
| sudo tee /etc/ssl/www.mi-dominio.com/www.mi-dominio.com.pem
Agregar certificado a la configuración de HAProxy
Al frontend que tenía asociado el puerto 80 agregamos el puerto 443, indicando que es conexión ssl y el certificado.
frontend fe-web
bind *:80
# Esto hace que HAProxy escuche el puerto 443 y utilice el certificado
bind *:443 ssl crt /etc/ssl/www.mi-dominio.com/www.mi-dominio.com.pem
Renovación del certificado
CertBot una vez ejecutado intentará la renovación del certificado 2 veces al día automáticamente mediante cron
o un timer
de systemd
. Sólo se renovará si el certificado ha expirado.
En caso de que se renueve, habrá que utilizar el nuevo certificado para generar el nuevo certificado para HAProxy. Una opción podría ser la automatización de la copia mediante cron
, pero puede dar lugar a fallos por falta de sincronismo entre la renovación y la generación.
Otra opción es forzar la generación de un nuevo certificado periódicamente, por ejemplo al comenzar el mes, y a continuación volver a generar el certificado para HAProxy.
El script sería:
#!/usr/bin/env bash
# Renovar certificado
certbot renew --force-renewal --tls-sni-01-port=8888
# Concatenar archivos
bash -c "cat /etc/letsencrypt/live/www.mi-dominio.com/fullchain.pem /etc/letsencrypt/live/www.mi-dominio.com/privkey.pem > /etc/ssl/www.mi-dominio.com/www.mi-dominio.com.pem"
# Recargar HAProxy
service haproxy reload
Si guardamos éste script como /opt/update-certs.sh
la tarea a crear en cron
para correrlo una vez al mes sería:
0 0 1 * * root bash /opt/update-certs.sh