Let's Encrypt sur HAProxy (Partie 1)
Vous vous en souvenez peut-être, nous avions effectué un tutoriel Let's Encrypt quelques jours après sa mise en bêta publique. D'ailleurs, il mériterait un petit relooking, vu que quelques détails ont un peu changé, et que d'autres clients ACME ont vu le jour. Par exemple on a honteusement passé sous silence l'excellent acme.sh qui a le mérite de ne pas demander une ribambelle de dépendance en temps que simple script Bash. De plus, le client principal (dont le nom est désormais Certbot) a désormais implémenté des extensions Apache et Nginx qui fonctionnent (presque) comme on le souhaite, à savoir modifier tout seul les configurations des sites pour fonctionner avec Let's Encrypt. Cependant je reste plus enclin à utiliser la méthode manuelle.
À l'époque, je vous parlais d'une infrastructure simple, à savoir un unique serveur frontal : c'est lui qui interceptait les requêtes et qui les traitait, point barre. L'infra d'un site avec une audience restreinte, car tout doit être traité par une unique machine. Bonjour l'indispo surprise.
Parlons à présent d'une architecture un peu plus ambitieuse, le genre de topologie qu'on peut trouver en production pour des sociétés importantes (et les technophiles enthousiastes). Cette première partie sera dédiée à l'explication de cette mystérieuse architecture et à la présentation d'une manière de la mettre en œuvre. Dans une seconde partie, on apprendra à y déployer ses certificats Let's Encrypt.
Souvent donc, on ne se contente pas d'un unique frontal, mais de plusieurs frontaux, sous la coupe d'un répartiteur de charge, (load-balancer en anglais) qui se chargera de réceptionner les requêtes des utilisateurs et de les distribuer aux frontaux, selon une stratégie définie. Cette architecture a quatre avantages :
- On gagne en charge maximale supportée, donc en nombre de visiteurs.
- Multiplier les frontaux amoindrit fortement les risques de crash du site, car en cas de panne d'un frontal, les autres frontaux se partagent le trafic.
- La mise en production d'une nouvelle version d'un site web peut se faire sans aucune interruption de service : il suffit de déployer sur un frontal à la fois.
- Les frontaux sont sur un réseau privés et non directement accessible sur le réseau global, donc la sécurité du site est renforcée.
On constate que le répartiteur de charge est un potentiel goulot d'étranglement et il nous faut donc un programme optimisé pour la haute disponibilité afin que le flux des requêtes ne soit pas le facteur limitant.
Disons maintenant que l'on souhaite apposer un certificat sur cette architecture. Question à dix sesterces : qui parmi ces serveurs doit porter ce certificat ?
Si on choisit de faire porter le certificat par les frontaux, cela signifie qu'il faut que tous les frontaux doivent le porter, et qu'il faudra déployer le certificat sur toutes ces machine à chaque renouvellement. Mais surtout, cela signifie que, le trafic étant chiffré jusqu'aux frontaux, le répartiteur de charge ne pourra pas prendre en compte le contenu de la requête dans sa stratégie de répartition, ce qui est fort pénalisant. En effet, il est coutume de configurer le répartiteur de charge pour distribuer certaines URL sur un pool de frontaux différent du pool normal, par exemple le back-office (la page d'administration et de saisie de contenu du site). Enfin dernier point, si vous vous souvenez du fonctionnement du protocole ACME comme décrit dans notre tutoriel, vous saurez que le certificat doit être généré sur la machine qui en fait la demande et qui répondra au challenge ACME. Ceci implique qu'un des frontaux doit être désigné pour la génération des certificat, et donc que le répartiteur de charge doit rediriger toutes les requêtes de type ACME vers ce frontal, ce qui est à la fois moche et justement impossible car notre répartiteur de charge ne peut pas comprendre ce qu'il redirige à cause du chiffrement.
La solution la plus sensée, qui maintient également le plus l'isométrie des frontaux, est de faire porter le certificat par le répartiteur de charge.
Zoomons alors sur cette fameuse machine, le répartiteur de charge. On va partir d'un socle OpenBSD. Pourquoi pas un Linux ? Parce qu'on utilise souvent la même machine pour la répartition de charge et pour le pare-feu, et qu'OpenBSD est le système de prédilection pour faire un routeur libre. Et en plus j'aime bien cet OS, ça change un peu les idées.
Sur ce socle, on va utiliser le répartiteur de charge HAProxy. Les seules tâches de HAProxy sont de porter le certificat du ou des sites (on parle de SSL-offloading) et d'aiguiller le trafic web sur les frontaux. Il ne peut pas générer de flux web par lui-même, il peut seulement relayer une requête à un autre service. Et d'ailleurs il est très bon pour ça.
HAProxy est organisé en frontends et en backends : un frontend est la façade présentée au monde extérieur qui va recevoir les requêtes des internautes. Un backend est un pool de serveurs frontaux qui va traiter la requête. HAProxy doit donc choisir sur quel backend envoyer une requête, mais il doit également gérer le pool de frontaux que représente ce backend.
Prenons un exemple de configuration simple. On passera les directives globales HAProxy qui ne sont pas intéressantes et présentent peu de valeur ajoutée. Et bien entendu, on part du principe que vos frontaux sont configurés, écoutent sur des adresses locales et tout le toutim.
frontend mon_super_site
bind *:80
mode http
acl url-back-office path_dir /admin
use_backend back-office if url-back-office
default_backend mes-frontaux
backend back-office
server backoffice 192.168.0.14:80
backend mes-frontaux
balance roundrobin
server backoffice 192.168.0.11:80
server backoffice 192.168.0.12:80
server backoffice 192.168.0.13:80
Dans cette configuration, le frontend écoute sur toutes les adresses IP attribuées au serveur, sur le port 80 (donc pour le moment, uniquement du trafic en clair). Le mode HTTP signifie qu'on utilise ce frontend pour répartir du flux web, et non pas comme le mode TCP ou l'on redirige un flux TCP intouché.
On a écrit une ACL, autrement dit une condition. Retenez cette notion car les ACL sont au cœur du principe de HAProxy. Ici notre ACL est vraie si le chemin de l'URL commence par /admin
et fausse sinon. On utilisera donc le backend back-office
si l'ACL est vraie et sinon, on utilisera le backend par défaut mes-frontaux
.
Le backend back-office
est très simple car il ne comporte qu'un serveur. Le backend mes-frontaux
comporte trois serveurs répartis en round-robin.
Et voilà, vous avez un répartiteur de charge. Si vous avez un certificat SSL, vous pouvez ajouter une ligne au frontend :
bind *:443 ssl cert /chemin/vers/mon/certificat.pem
Sauf que ça ne marchera pas. En tout cas pas tant que vous aurez mis votre certificat sous la forme attendue par HAProxy, à savoir la clef privée et le certificat dans le même fichier. Nous verrons cela dans la seconde partie, qui vous expliquera également (et finalement) comment diable utiliser Let's Encrypt avec ce bidule. En tout cas, si vous êtes perspicace, vous vous doutez que ça tournera en partie autour des ACL, car il y a bien une raison pour laquelle c'est le seul élément en gras de l'article.
(Source de l'image d'en-tête : Great A'tuin par Schiraki)