Je vais essayer de poser les bases de manière simple, ce n'est pas gagné !
Tout d'abord petit rappel de ce qu'est haproxy: C'est un logiciel open source qui gère le load balancing et le reverse proxy. Pour ma part je n'utilise que la partie reverse proxy. Depuis une seule adresse IP je vais redistribuer des noms de domaines vers mes serveurs internes. Enfin, deux adresses IP, une IPv4 et une IPv6.
J'ai au préalable configurer chez mon hébergeur (qui n'héberge plus rien) plusieurs noms de domaine ou sous-domaine, soit en DynHost pour les Ipv4, soit en champs AAAA pour les IPv6, avec en plus un champs CAA pour Let's Encrypt:
chez IN CAA 128 issue "letsencrypt.org"
chez IN AAAA xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
Ensuite mon routeur-firewall redirige les ports 80 et 443 vers mon serveur haproxy. En réalité c'est un peu plus compliqué que cela, aillant viré la Livebox au profit d'une VM sous Mikrotik Cloud Hosted Router qui sert de passerelle à la fois vers quelques VM et vers mon routeur domotique qui fait lui aussi passerelle vers quelques VM ! Bref, je m'y retrouve c'est l'important. Je parle de ça car par exemple pour la VM OpenVpn les port 80 et 443 passe par la VM haproxy mais les ports 993 et 1194 passe par le routeur domotique. Pour faire simple tout ce qui est port 80 et 443 est dirigé vers haproxy, peut-importe le nom de domaine devant.
;;; exemple simpliste en Nat qui permet de tester rapidement
;;; vers haproxy en http
/ip firewall nat chain=dstnat action=dst-nat to-addresses=10.1.5.111 to-ports=80 protocol=tcp in-interface=WAN dst-port=80 log=no log-prefix=""
;;; vers haproxy en https
/ip firewall nat chain=dstnat action=dst-nat to-addresses=10.1.5.111 to-ports=443 protocol=tcp in-interface=WAN dst-port=443 log=no log-prefix=""
;;; vers openvpn
/ip firewall nat chain=dstnat action=dst-nat to-addresses=10.1.10.111 to-ports=1194 protocol=udp in-interface=WAN dst-port=1194 log=no log-prefix=""
Ensuite haproxy gère donc la distribution des sites web en fonction du nom de domaine appelé. Il gère aussi tout les certificats SSL de ces machines, c'est très pratique et ça simplifie la création de Virtual Host ensuite. J'ai dit tous les certificat SSL ? Non. Tous sauf un, celui de OpenVpn qui est géré par la VM OpenVpn elle même car elle l'enregistre en dur dans sa base de donnée… C'est là que tout se complique dans la configuration de haproxy: Savoir gérer les certificats SSL de certains sites mais d'en laisser d'autres être gérés par leurs VM.
Pour les certificats utilisés par haproxy je suis passé par Cerbot, Let's Encrypt et un petit script Bash de renouvellement et compactage.
Je ne vais pas expliquer comment installer une VM, haproxy, configurer un nom de domaine, etc, ce n'est pas le sujet ici qui est uniquement de me faire un mémo sur le fichier de configuration /etc/haproxy/haproxy.cfg
. Let's go !
# Configuration valable pour haproxy 2.2.x
# Section global
global
# Configuration des logs, de l'utilisateur system, des statistiques.
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket :9999 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Configuration des niveaux de certificats acceptés pour SSL.
# Ces niveaux sont ceux de la certification A+ à juillet 2022.
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA>
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-R>
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
# Section defaults
defaults
# Par default on travail en layer 7 (http)
mode http
option httplog
log global
option dontlognull
# Par default on transmet l'IP cliente
option forwardfor
# On configure divers options de timeout et d'erreurs
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
# Section stats
# On peut complètement enlevé les stats ce n'est pas nécessaire au fonctionnement du proxy.
# On écoute sur http://mon_ip:22222/haproxy?stats
listen stats
mode http
bind :22222
stats enable
stats uri /haproxy?stats
stats realm Haproxy\ Statistics
stats auth un_utilisatuer:un_mot_de_passe
stats refresh 30s
# Frontend HTTP
frontend frontend_http
# On écoute sur le port 80 en IPv4 et IPv6
bind :::80 v4v6
# On regarde si c'est une vérification pour un renouvèlement de certificat dédié à OpenVpn
acl acl_domain_openvpn hdr_end(host) -i openvpn.mondomaine.fr
# On regarde si c'est une vérification pour un renouvèlement de certificat haproxy
acl acl_letsencrypt_ha path_beg /.well-known/acme-challenge/
# On utilise le backend spécial OpenVpn
use_backend backend_openvpn_http if acl_domain_openvpn
# On utilise le backend spécial Let's Encrypt
use_backend backend_letsencrypt_ha if acl_letsencrypt_ha !acl_domain_openvpn
# On redirige tout les reste vers HTTPS
redirect scheme https code 301 if !acl_letsencrypt_ha !acl_domain_openvpn
# Frontend HTTPS
frontend frontend_https_all
# On écoute sur le port 443 en IPv4 et IPv6
bind :::443 v4v6
# On travail en layer 4 (tcp) pour outrepasser les certificats non géré par haproxy
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
# On utilise un bakcend pour OpenVpn
use_backend backend_proxy_https_openvpn if { req_ssl_sni -i openvpn.mondomaine.fr }
# Sinon on utilise un backend pour haproxy
default_backend backend_proxy_https_ha
# Backend proxy pour OpenVpn en HTTPS
backend backend_proxy_https_openvpn
mode tcp
# On utilise haproxy lui-même comme serveur
server loopback-for-tls abns@haproxy_openvpn send-proxy-v2
# Backend proxy pour tout le reste en HTTPS
backend backend_proxy_https_ha
mode tcp
# On utilise haproxy lui-même comme serveur
server loopback-for-tls abns@haproxy_ha send-proxy-v2
# Frontend proxy OpenVpn en HTTPS
frontend frontend_https_openvpn
# On dit a haproxy que ça vient de lui-même
bind abns@haproxy_openvpn accept-proxy
mode tcp
option tcplog
log global
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
# On redirige vers le backend OpenVpn sans vérifier de certificat
use_backend backend_openvpn_https if { req.ssl_sni -i openvpn.mondomaine.fr }
frontend frontend_https_others
# On écoute en provenence du proxy et on utilise les certificat haproxy
bind abns@haproxy_ha accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
# On nettoie et ajoute quelques entête à signaler à nos serveurs web
http-request del-header X-Forwarded-For
http-request add-header X-Forwarded-Proto https
http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
# Petite astuce pour CalDav de nextcloud
acl acl_nextcloud_url_discovery path /.well-known/caldav /.well-known/carddav
http-request redirect location /remote.php/dav/ code 301 if acl_nextcloud_url_discovery
# On distribue nos noms de domaines
use_backend backend_openvpn_https if { hdr_end(host) -i openvpn.mondomaine.fr }
use_backend backend_a_mondomaine_fr if { hdr_end(host) -i a.mondomaine.fr }
use_backend backend_b_mondomaine_fr if { hdr_end(host) -i b.mondomaine.fr }
use_backend backend_mondomaine_com if { hdr_end(host) -i z.mondomaine.com }
use_backend backend_multidom if { hdr_reg(host) ^xyz.tld|a.xyz.tld|c.mondomaine.fr$ }
default_backend backend_mondomain_com
# Backends
# Pour les domaines certifiés par haproxy on utilise que le port 80
# server a
backend backend_a_mondomaine_fr
description Blablabla a.domaine.fr
server server_a_domaine_fr 10.1.5.120:80 check
# server b
backend backend_b_mondomaine_fr
description Blablabla b.domaine.fr
server server_b_domaine_fr 10.1.5.121:80 check
# server c
backend backend_mondomaine_com
description Blablabla domaine principal
server server_b_domaine_fr 10.1.5.122:80 check
# server d
backend backend_multidom
description Multi domaines
server server_multidom 10.1.5.123:80 check
# Cas particuliers
# renouvelement letsencrypt certbot
backend backend_letsencrypt
description Cerbot renew
# On utilise le server certbot qu'on a crée sur le même serveur que haproxy
server server_letsencrypt 127.0.0.1:8888
# renouvelement letsencrypt openvpn sur sa machine dédié
backend backend_openvpn_http
description Openvpn certbot renew
server server_openvpn_http 10.1.10.222
# server openvpn (web GUI) qui gère son propre certificat SSL
backend backend_openvpn_https
description Openvpn web UI
mode tcp
option ssl-hello-chk
server server_openvpn_https 10.1.10.222:443
C'est une version très simplifié de mon fichier de configuration il y a surement quelques erreurs que vous ne manquerez pas de me signaler. Et j'essayerais de mettre à jour en fonction de mes avancement en connaissance sur haproxy.