Configurer HAProxy avec LetsEncrypt et plusieurs sous-domaines
Voici une méthode pour voir comment configurer haproxy et letsencrypt et gérer facilement https avec docker.
Nous avons un serveur qui héberge plusieurs services liés à des sous-domaines différents et qui doivent tous avoir un certificat.
En prérequis, les entrées DNS des sous-domaines pointent toutes sur le même serveur.
Sur un serveur qui héberge plusieurs services en https sur des sous-domaines différents, voici une méthode (parmi d'autres) pour configurer HAProxy avec des certificats LetsEncrypt qui se renouvellent automatiquement.
- Reprendre le fichier /etc/haproxy/haproxy.cfg ci-dessous et compléter avec votre domaine et services :
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
tune.ssl.default-dh-param 2048
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
# An alternative list with additional directives can be obtained from
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend http-in
bind *:80
acl http ssl_fc,not
http-request redirect scheme https if http
frontend https-in
bind *:443 ssl crt-list /etc/haproxy/certs/domains_list.txt
option forwardfor
option forwardfor header X-Real-IP
http-request add-header X-Real-IP %[src]
reqadd X-Forwarded-Proto:\ https
acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt-back if letsencrypt-acl
acl host_dom1 hdr_end(host) -i subd1.mydomain.com
acl host_dom2 hdr_end(host) -i subd2.mydomain.com
use_backend dom1-back if host_dom1
use_backend dom2-back if host_dom2
default_backend dom1-back
backend dom1-back
redirect scheme https if !{ ssl_fc }
server dom1 127.0.0.1:8080 check
backend dom2-back
redirect scheme https if ! { ssl_fc }
server dom2 127.0.0.1:9980 check
backend letsencrypt-back
server letsencrypt 127.0.0.1:8888
- Pour créer un certificat la première fois, exécutez la commande :
certbot certonly --standalone -d subd2.mydomain.com --email me@email.com --agree-tos --non-interactive --http-01-port=8888
- Concaténez les fichiers de certificats en un seul fichier pour haproxy :
cat /etc/letsencrypt/live/subd2.domain.com/fullchain.pem /etc/letsencrypt/live/subd2.domain.com/privkey.pem | tee /etc/haproxy/certs/subd2.domain.com.pem
- Créez ou complétez le fichier /etc/haproxy/certs/domains_list.txt :
/etc/haproxy/certs/subd1.domain.com.pem subd1.domain.com
/etc/haproxy/certs/subd2.domain.com.pem subd2.domain.com
- Redémarrez haproxy pour prendre en compte les ajouts
sudo haproxy reload
- Pour le renouvellement, copiez le script ci-dessous dans
/opt/certif-renewal.sh
et ajoutez le en crontab :
#!/bin/sh
certbot renew --force-renewal --tls-sni-01-port=8888
cat /etc/letsencrypt/live/subd1.domain.com/fullchain.pem /etc/letsencrypt/live/subd1.domain.com/privkey.pem | tee /etc/haproxy/certs/subd1.domain.com.pem
cat /etc/letsencrypt/live/subd2.domain.com/fullchain.pem /etc/letsencrypt/live/subd2.domain.com/privkey.pem | tee /etc/haproxy/certs/subd2.domain.com.pem
service haproxy reload
echo `date +'%F %T'` " ---- Certificat renewal done ----"
Et voilà...
Explications
HAProxy est un load balancer très performant et simple de mise en oeuvre.
Dans ce mode de fonctionnement, les appels vers le serveur et les sous-domaines sont forcés en https par haproxy et c'est lui qui gère les certificats. Les appels aux services backend sont alors fait en http. Cela facilite leur mise en oeuvre car vous évite de gérer des certificats, surtout quand ces services sont des containers docker.
Le problème général est que haproxy écoute sur le port 80 et 443 de votre serveur et que certbot a besoin de ces ports pour créer les certificats ou les renouveler. Vous pourriez stopper haproxy le temps de faire le renouvellement mais ici nous préférons utiliser certbot sur un port dédié (8888 par exemple) et donner l'instruction à HAProxy de l'utiliser quand il détecte l'appel à l'URL de LetsEncrypt.
Du coup, pour les commandes certbot, il faut ajouter l'attribut --http-01-port=8888
pour préciser sur quel port se connecter. Celui-ci correspond à ce que vous paramétrez dans la configuration du backend de HAProxy.
Dans la section global, l'instruction tune.ssl.default-dh-param 2048
augmente la sécurité lors de l'échange de clés (1024 par défaut).