Introduction aux reverse proxies
Pour rendre votre service web accessible au public, il est courant que celui-ci se retrouve derrière un « reverse proxy ». Ce dernier sert à centraliser la sécurité, à répartir les accès publics sur plusieurs serveurs, ou à équilibrer la charge entre plusieurs serveurs identiques (pour permettre plus de charge, ou pour de la haute disponibilité). Le reverse proxy permet aussi de meilleures performances s’il met en cache les données statiques (images, logos, fonts, JS, CSS).
Dans cet article, nous vous proposons un ensemble d’outils pour mettre en place un reverse proxy, qui gère le HTTPS.
Configuration de HAproxy
J’utilise plusieurs reverse proxies : HAProxy, Nginx, Traefik pour l’essentiel. Aujourd’hui, ce sera HAProxy !
Un serveur web est joignable sur les ports 80 (HTTP) et 443 (HTTPS). Le nom de domaine mon-app.fr pointe vers mon reverse proxy, qui va ensuite faire un rebond en HTTP vers deux serveurs web capables de répondre aux visiteurs.

Sur le serveur qui fait reverse proxy, j’installe HAProxy avec sudo apt install haproxy. Si j’ai un pare-feu, je pense à ouvrir les ports 80 et 443.
Voilà. On a un reverse proxy fonctionnel mais pas configuré. On va faire ça rapidement, mais avant, un petit point sur le HTTPS.
La gestion des certificats SSL depuis un reverse proxy
La gestion des certificats SSL (qui permet au HTTPS de chiffrer les communications) doit être faite par HAProxy. Parce que les serveurs web capables de répondre à un challenge ACME se trouvent ailleurs dans un réseau privé d’une part, et parce que dans un environnement de load balancing, c’est très important de maintenir une configuration homogène entre les différents serveurs. Donner plus de pouvoir à un des backends pour le renouvellement d’un certificat SSL romprait cette homogénéité. En plus, comme on redirige une requête sur deux vers chaque serveur, la génération de certificats SSL, nécessaire au HTTPS, pourrait poser problème. En effet, je suis incapable de prédire si les robots de validation de l’ACME vont visiter le bon serveur.
On va donc joindre l’utile à l’agréable. Plutôt cool, non ?
Pour commencer et organiser un peu, je fais une arborescence dans le dossier /etc/haproxy : mkdir -p /etc/haproxy/ssl. C’est là que je vais stocker mes certificats. Puis je configure HAProxy en éditant le fichier /etc/haproxy/haproxy.cfg en ajoutant ceci :
# Ouverture du port 80 (HTTP) au public
frontend http
bind *:80
mode http
option httplog
# Redirection automatique vers HTTPS si l'URL n'est pas utilisée pour le challenge ACME
http-request redirect scheme https code 301 if !{ path_beg /.well-known/acme-challenge/ }
# Si l'URL appelée est faite pour valider un certificat, alors je renvoie vers un backend spécifique
use_backend backend-certbot if { path_beg /.well-known/acme-challenge/ }
# Ouverture du port 443 (HTTPS) au public
frontend https-tcp
bind *:443 ssl crt /etc/haproxy/ssl/ crt-ignore-err all
mode http
option httplog
# Si l'URL appelée est faite pour valider un certificat, alors je renvoie vers mon backend spécifique
use_backend backend-certbot if { path_beg /.well-known/acme-challenge/ }
# Sinon, si je détecte que c'est le nom de domaine de mon-app.fr, je redirige vers le backend "mon-app"
use_backend mon-app if { hdr(host) -i mon-app.fr }
# Backend dédié au renouvellement de certificats
backend backend-certbot
mode http
server certbot 127.0.0.1:2031
#Bakend de mon app
backend mon-app
mode http
balance roundrobin
server Serveur_A 192.168.10.1:80
server Serveur_B 192.168.10.2:80
Attention :
- Pensez à changer les noms de domaines pour les vôtres.
- N’oubliez pas de renommer les backends. Votre site ou votre API n’est certainement pas « mon-app » 😊.
- Changez les IPs des backends pour les vôtres.
- Si vous n’avez qu’un seul backend, retirez la ligne
balance roundrobinet ne laissez qu’une seule ligne de définition du serveur (comme dansbackend-certbot). - Vous pouvez ajouter plusieurs règles de détection de nom de domaine dans le frontend HTTPS, il faut alors aussi faire autant de backends (sauf si plusieurs noms de domaines pointent vers le même service web).
Si vous découvrez HAProxy, vous avez probablement l’impression que c’est de la sorcellerie. Mais si vous connaissez, vous noterez que la seule subtilité, c’est d’identifier les requêtes à destination de Certbot (l’outil qui va commander les certificats) pour les renvoyer sur le port 2031 du serveur reverse proxy. Ce port n’a pas besoin d’être ouvert sur votre pare-feu, c’est pour que deux services (HAProxy et Certbot) puissent se parler sur le même serveur.
Maintenant, on passe à la partie gestion des certificats.
Commander un certificat pour mon nom de domaine
Pour s’organiser encore, j’ajoute un nouveau dossier dans /etc/haproxy. Ce dernier va stocker mes scripts qui permettent de piloter Certbot. J’y reviens juste après.
mkdir -p /etc/haproxy/scripts
🧌 etc signifie « Editable Text Configuration ». Là, tu me mets du script au beau milieu de la config ! gngngn (troll qui rage)
🤓 Oui, oui, je sais ! Ce que je m’apprête à faire n’est pas des plus joli, mais vous pouvez tout à fait déplacer ça où bon vous semble. Même si je suis le premier à militer pour que l’on respecte les responsabilités des dossiers sur mes serveurs (travail d’équipe oblige, on doit avoir des normes communes), je précise pour ma défense qu’il n’y a pas de norme ou de RFC spécifique qui définit le contenu du dossier « /etc » sur un système de fichiers Linux. Et oui, je sais : c’est de la mauvaise foi 😜
Maintenant, on va mettre en place un script qui fait une demande de certificats à Let’s Encrypt. Mettez-le où bon vous semble, moi ce sera /etc/haproxy/scripts/request-certificate.sh. Le script suivant va faire plusieurs choses :
- Vérifier qu’on lui donne bien un argument (le nom de domaine pour lequel on souhaite un certificat)
- Vérifier que Certbot, l’outil qui permet de demander un certificat, est bien installé
- Demander un certificat pour notre domaine
- En précisant que c’est standalone
- Que l’on va prouver qu’on est bien admins de ce nom de domaine par un challenge ACME en HTTP sur le port 2031 (vous pouvez changer ça, mais attention, je l’utilise aussi dans la configuration HAProxy plus loin dans cet article)
- Qu’on accepte les conditions d’utilisation de Let’s Encrypt
- Que c’est une action à l’initiative d’un script, et que donc on préfère que tout soit automatique, et qu’il n’y ait pas de réponses à fournir.
- Préparer la chaîne de certification que HAProxy sait lire (un fichier avec le certificat public et privé dedans) et l’enregistrer dans /etc/haproxy/ssl
Pensez bien à modifier le script pour y mettre votre adresse email à la place de your@mail.here .
#!/bin/bash
# Vérification que l'argument DOMAIN est fourni
if [ -z "$1" ]; then
echo "Usage: $0 <domaine>"
echo "Exemple: $0 exemple.com"
exit 1
fi
EMAIL=your@mail.here
DOMAIN=$1
if ! command -v certbot &> /dev/null
then
echo "certbot not found PATH. Is certbot installed ? Please refer to https://certbot.eff.org/instructions "
exit 1
fi
certbot certonly \
--standalone \
--preferred-challenges http \
--http-01-address 127.0.0.1 \
--http-01-port 2031 -d $DOMAIN \
--email $EMAIL \
--agree-tos \
--non-interactive
cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/letsencrypt/live/$DOMAIN/privkey.pem > /etc/haproxy/ssl/$DOMAIN.pem
Et puis rendez ce script utilisable avec la commande chmod +x /etc/haproxy/scripts/request-certificate.sh. Ceci permet de lancer le script.
Que fait ce script ? Il vérifie que vous le lancez avec un nom de domaine spécifié, puis il demande à Let’s Encrypt un certificat pour le nom de domaine. Pour vérifier que vous êtes bien légitime, Let’s Encrypt va vous donner un mot aléatoire et trèèèèèèèèèèèès long à mettre à disposition très rapidement sur votre site à un endroit bien particulier. Le script ouvre donc le port 2031 (utilisé comme backend dans HAProxy, rappelez-vous), et y place ce fichier de challenge. Quand Let’s Encrypt va envoyer un robot vérifier que le fichier est bien là, HAProxy va renvoyer la demande à Certbot qui l’a sur lui. En fait, ça permet de ne pas avoir réellement besoin d’avoir un serveur web disponible pour commander un certificat.
Reste à installer le fameux Certbot : sudo apt install certbot
Voilà ! Vous pouvez relancer HAProxy avec systemctl restart haproxy puis lancer le script pour avoir votre premier certificat. Pour mon-app.fr, la commande sera /etc/haproxy/scripts/request-certificate.sh mon-app.fr
Renouvellement automatique du certificat
Si vous ne le saviez pas, Let’s Encrypt fournit gratuitement un certificat SSL signé par une autorité de certification (CA) reconnue par les navigateurs. Seulement voilà, les certificats ne sont valables que trois mois. Il faut donc les renouveler avant expiration. Pour ça, j’ai fait d’autres scripts à placer à côté du premier dans /etc/haproxy/scripts/renew-certificate.sh
#!/bin/bash
certbot renew \
--standalone \
--force-renewal \
--preferred-challenges http \
--http-01-address 127.0.0.1 \
--http-01-port 2031 \
--post-hook "/etc/haproxy/scripts/concatenates.sh && systemctl reload haproxy.service" \
--quiet
et /etc/haproxy/scripts/concatenates.sh
#!/bin/bash
for DOMAIN in `find /etc/letsencrypt/live/* -type d`; do
DOMAIN=`basename $DOMAIN`
cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/letsencrypt/live/$DOMAIN/privkey.pem > /etc/haproxy/ssl/$DOMAIN.pem
done
Ici, c’est très semblable au script précédent, ça fait la même chose, mais cette fois-ci, on précise que c’est un renouvellement que l’on souhaite. Au passage, si vous aviez utilisé le script précédent pour 1, 2, 10, 100 noms de domaines différents, le renouvellement va se faire pour chacun d’eux.
Reste à rendre ce script exécutable : chmod +x /etc/haproxy/scripts/renew-certificate.sh /etc/haproxy/scripts/concatenates.sh
Puis à planifier le lancement automatique de ce script régulièrement. Pour ça, on va faire une tâche planifiée, et sous Linux, ça s’appelle une tâche CRON.
Vérifiez que cron est bien installé : sudo apt install cron puis entrez dans l’éditeur de cron sudo crontab -e
0 12 1 * * /etc/haproxy/scripts/renew-certificate.sh
Ajoutez la ligne ci-dessus. Elle va planifier un renouvellement tous les premiers du mois, à 12h00. Vous pouvez changer la fréquence comme bon vous semble. Enregistrez et quittez.
Retirer un certificat
C’est vraiment pas mal tout ce qu’on a fait, mais il reste un petit problème… Si j’arrête d’utiliser un nom de domaine, ou que je souhaite qu’il pointe vers un autre serveur, alors mon renouvellement ne fonctionnera plus pour ce domaine. J’aurai des échecs à chaque tentative. Pour ça, il faut pouvoir indiquer qu’on ne souhaite plus renouveler un certificat à Certbot. J’ai donc un dernier script pour vous qui fait ce travail à votre place.
Copiez donc ceci dans /etc/haproxy/scripts/revoke-certificate.sh
#!/bin/bash
# Vérification que l'argument DOMAIN est fourni
if [ -z "$1" ]; then
echo "Usage: $0 <domaine>"
echo "Exemple: $0 exemple.com"
exit 1
fi
DOMAIN=$1
# Confirmation avant suppression du certificat
echo "Suppression du certificat pour le domaine : $DOMAIN"
read -p "Êtes-vous sûr de vouloir supprimer ce certificat ? [y/N]: " confirmation
if [[ "$confirmation" =~ ^[Yy]$ ]]; then
certbot delete --cert-name $DOMAIN
echo "Certificat supprimé pour $DOMAIN."
else
echo "Opération annulée."
fi
Encore une fois, rendez ce script exécutable avec chmod +x /etc/haproxy/scripts/revoke-certificate.sh
Pour renoncer à un nom de domaine déjà configuré dans Certbot, il faut lancer /etc/haproxy/scripts/revoke-certificate.sh mon-app.fr
