Notas de Modri

HAProxy + Let's Encrypt

Sun 09 February 2020 / haproxy tls ssl

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

Referencias

Modri

A cerca de Modri

Geek. Coder. Google-Fu practicioner. Tech Lead in progress. Opinions are my own.

Comments