Un reverse-proxy est un serveur HTTP qui effectue l’intermédiaire entre les clients web (les navigateurs) et les services web de l’association (outils numériques libres, sites statiques…).
Sans reverse-proxy, il n’est pas possible d’allouer un même port sur une même IP par plusieurs services web − or, chacun de nos quatre serveurs ne dispose que d’une seule IPv4 chacun pour plusieurs dizaines de services accessibles depuis de nombreux sous-domaines.
Centraliser la gestion du trafic web sur un seul service permet de concentrer également la gestion des certificats TLS et des journaux de connexion, ce qui simplifie grandement l’administration des services.
Après plusieurs années d’utilisation de nginx, nous avons finalement peu à peu migré vers Caddy comme reverse-proxy alternatif. Il présente plusieurs avantages à nginx :
La Contre-Voie a historiquement utilisé nginx comme reverse-proxy depuis la mise en ligne de ses services. En 2021, par intérêt pour les optimisations possibles du transit HTTP dans des versions modifiées de nginx, nous avons entrepris de déployer HTTP/3 en production. Ce premier déploiement était un échec, l’implémentation de HTTP/3 sur nginx étant incomplète et peu fonctionnelle à ce moment-là.
Le 25 mars 2023, nous avons peu à peu adopté Caddy comme reverse-proxy alternatif à nginx, au départ uniquement sur notre machine dédiée à Nitter.
Le 3 juillet 2023, nous avons déployé Caddy sur toutes nos machines, remplaçant ainsi définitivement nginx.
Nous utilisons une image modifiée de Caddy à partir de son image officielle, library/caddy, tag 2-builder
. Cette image incorpore les outils nécessaires pour recompiler Caddy depuis ses sources. Malheureusement, en Go, l’ajout de modules au programme nécessite la recompilation du binaire.
Retrouvez son Dockerfile dans notre dépôt Core.
Une partie de ces modules supplémentaires sont absolument indispensables pour nos besoins :
caddyserver/transform-encoder
, qui permet d’écrire des journaux de connexion au format standard (similaire à ceux de nginx) et de les faire transiter sur le réseau : nous avons besoin de les envoyer via UDP sur notre serveur de journalisation ;
caddy-dns/ovh
: renouvelle nos certificats par validation DNS plutôt que via webroot. Les deux sont possibles, mais cette solution permet de générer des certificats wildcard, qui pourraient être plus pertinents pour notre usage avec de nombreux sous-domaines.mholt/caddy-ratelimit
: ajoute de nombreuses options pour limiter et bloquer le trafic entrant pour préserver le fonctionnement de nos services.En tant que reverse-proxy, Caddy a besoin d’accéder à tous les conteneurs qui exposent une interface web, ce qui implique qu’il se connecte à de nombreux sous-réseaux. Cette disposition peut ralentir le lancement et le redémarrage du conteneur à l’usage.
Caddy nécessite de nombreux volumes par défaut :
autosave.json
) monté sur /config
/data
/etc/localtime
)/etc/caddy/
)Pour que Caddy puisse servir directement les assets des sites statiques, les volumes contenant ces fichiers sont montés directement dans son conteneur en lecture seule.
Lorsque c’est possible, les ressources et les pages sont compressées au préalable (format Brotli) pour accélérer le traitement des requêtes.
Il y a deux manières de tester et déployer une nouvelle configuration de Caddy :
COPY
dans le Dockerfile, puis une instruction RUN caddy validate --config …
qui va vérifier la validité de cette configuration au build-time ;caddy validate --config …
dans le conteneur de caddy, puis recharger le daemon dans le conteneur.La première méthode a l’inconvénient de nécessiter un redémarrage du conteneur à chaque modification. Le conteneur étant connecté à de nombreux réseaux, ce redémarrage peut prendre du temps.
La deuxième méthode nécessite de se rendre dans /var/lib/docker/volumes/
avec les droits root pour effectuer les ajustements nécessaires.
Caddy consomme une quantité plutôt raisonnable de RAM : entre 100 et 250 Mo pour le transit de tous nos services, ce trasit pouvant atteindre plusieurs téraoctets par semaine à cause de notre service Nitter. Il consomme peut-être un peu plus de CPU que nginx mais la différence n’est pas particulièrement perceptible.
L’installation de HTTP/3 doit absolument être testée, ce qui n’est pas forcément facile : à ce jour, curl ne supporte pas nativement ce protocole, et Firefox utilise HTTP/2 si HTTP/3 met trop de temps à répondre.
Cependant, si votre serveur HTTP/3 est mal configuré et ne répond pas aux requêtes, certains navigateurs pourraient attendre la réponse de votre serveur pendant plusieurs secondes, dégradant sévèrement l’expérience utilisateur·ice.
Quelques éléments importants à vérifier sur votre installation :
alt-svc h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
doit bien être configuré pour utiliser le port 443 (et pas 8443, ce qui peut être le cas si votre serveur Caddy tourne sans privilèges dans son conteneur) ;L’onglet Réseau des outils de développement de Firefox est un indicateur de présence de HTTP/3, mais pourrait tout de même fallback en HTTP/2 pendant vos tests à cause de problèmes de latence. Il est préférable de tester avec plusieurs navigateurs et si possible, avec une version recompilée de curl qui supporte ce protocole.
Si vous déployez pour la première fois un serveur web avec de nombreux sous-domaines, vous pourriez vous retrouver confronté·es à un problème de rate limit. Consultez la politique de rate limit de Let’s Encrypt avant de déployer votre configuration.
Caddy propose également des certificats ZeroSSL gratuits. Il est toutefois bon de noter que Caddy est financé et maintenu par ZeroSSL.
Caddy en lui-même ne nécessite pas beaucoup d’entretien et fonctionne en autonomie, même s’il est nécessaire de porter attention aux journaux d’erreurs du reverse-proxy.
La mise à jour s’effectue en téléchargeant la variante « builder » de l’image officielle et en recompilant Caddy, puis en redémarrant le conteneur.
Pour répartir équitablement la charge du trafic entre plusieurs machines, mais aussi potentiellement pour mitiger le point de défaillance unique que constitue Caddy, il pourrait être envisagé de déployer une configuration où il existe plusieurs instances de Caddy qui partageraient les mêmes fichiers de configuration.