AccueilTechniqueServices auxiliaires → Reverse-proxy

    Reverse-proxy

    Fonctionnement🔗

    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…).

    Pourquoi utiliser un reverse-proxy ?🔗

    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.

    Pourquoi Caddy plutôt que nginx ?🔗

    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 :

    • Support HTTP/3 natif ;
    • Sélection d’algorithmes et de protocoles sécurisés par défaut pour la session TLS (avec nginx, il était nécessaire de les spécifier manuellement dans la configuration) ;
    • Gestion automatique de l’agrafage OCSP (qui était jusqu’à présent réalisée par une cron) ;
    • Renouvellement automatique des certificats (également gérées par une cron auparavant) ;

    Historique🔗

    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.

    Installation🔗

    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 ;
      • Par défaut, Caddy conserve des journaux structurés en JSON, ce qui n’est pas du tout pratique à stocker.
    • 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 :

    • pour sa configuration (essentiellement pour le fichier autosave.json) monté sur /config
    • pour ses données relatives aux certificats TLS monté sur /data
    • pour récupérer le fuseau horaire de l’hôte (/etc/localtime)
    • pour ses Caddyfiles le cas échéant (sur /etc/caddy/)
    • et d’éventuels volumes supplémentaires pour des sites statiques à servir.

    Sites statiques🔗

    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.

    Tests🔗

    Il y a deux manières de tester et déployer une nouvelle configuration de Caddy :

    1. Modifier la configuration et reconstruire l’image Docker en incorporant les nouveaux fichiers avec une instruction COPY dans le Dockerfile, puis une instruction RUN caddy validate --config … qui va vérifier la validité de cette configuration au build-time ;
    2. Modifier les fichiers de configuration montés dans un volume et exécuter 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.

    Consommation de ressources🔗

    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.

    Précautions🔗

    HTTP/3🔗

    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 :

    • Le port UDP 443 doit être ouvert et bien lié au conteneur de Caddy (autant en IPv4 qu’en IPv6) ;
    • L’en-tête 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) ;
    • Des testeurs en ligne peuvent être utilisés pour vérifier le fonctionnement de HTTP/3, mais leur fiabilité est limitée.

    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.

    Certificats et rate limits🔗

    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.

    Entretien🔗

    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.

    Mise à jour🔗

    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.

    Évolutions envisagées🔗

    Déploiement en cluster🔗

    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.