Journal Recherche d'un reverse proxy

Posté par  (site Web personnel) . Licence CC By‑SA.
Étiquettes :
12
5
mar.
2021

Hello world.

Cela fait longtemps que je n'ai pas pris ma plume pour vous parler. Le temps et l'envie me manquaient.

Aujourd'hui je crée ce journal car je me demande quel reverse proxy utiliser.

Jusqu'à maintenant, j'ai utilisé Lighttpd puis Traefik proxy. J'ai switché sur Trafik par ce qu'il est simple à configurer, que sa configuration peut se faire via des fichiers YAML (pratique si on déploie sa config avec Ansible), sa configuration est dynamique, il peut servir de proxy TCP et il peut également récupérer tout seul des clés/certificats avec Let's Encrypt. (Plus d'info sur le site web officielle)

Mais Traefik a ses défauts: L'image de conteneur officielle exécute Traefik avec l'utilisateur ROOT, les clés/certificats Let's Encrypt sont stockés dans un fichier json (pas l'idéal pour les ré-utiliser avec d'autres services), la documentation incite à laisser à Traefik un accès au socket de Docker
et il existe une version propriétaire (pas fan de cette méthode). Ces deux derniers points me font hésiter à le suggérer à d'autres personnes.

Récemment un amis m'a présenté Caddy. Il a l'aire simple, efficace, il récupère automatiquement les clés/certificats Let's Encrypt, il effectue automatiquement la redirection HTTP vers HTTPS, on peut découper sa configuration en petits fichiers, il propose une API rest pour le configurer, HTTP 3 et websocket sont au menu et il est modulaire. (Plus d'info sur le site web officielle)

Mais Caddy a aussi ses défauts: L'image de conteneur officielle exécute aussi Caddy avec l'utilisateur ROOT et il y a des conditions dans sa licence que n'en faisait pas un logiciel libre (je ne sais pas si c'est réglé).

Au détour d'articles, j'ai parfois lut d'autre nom de reverse proxy mais je ne les ai pas noté. Maintenant je me demande: Quel reverse proxy choisir ? Est-ce que vous auriez des suggestions ?

Voici ce que je recherche:
- Simple
- Logiciel Libre
- Si possible, pas de version proprio
- Load balancing intégré
- HTTP et Websocket
- Récupération automatique des clés/certificats Lets Encrypt avec
possibilité de les ré-utiliser avec d'autres services (mail, XMPP,
etc)
- Configuration dynamique avec des fichiers
- Image de conteneur officielle, à jour et où le logiciel ne tourne
pas en tant que ROOT

Merci pour vous retours.

  • # Forums

    Posté par  (site Web personnel) . Évalué à 10 (+9/-0). Dernière modification le 05/03/21 à 13:57.

    Ce journal extrèmement détaillé, qui pose une bonne question, me rappelle que les forums de LinuxFr sont vraiment un parent pauvre, mal vendu et poussiereux. Je parie que c'est un peu une épine dans le pied de l'équipe, je veux dire il faudrait carrément monter une rubrique comme "meta" dans Stack Overflow, où on (tout le monde, les rédacteurs, les modérateurs, les lecteurs, tout le monde) discuterait de ce que cette section pourrait devenir, y compris rien, oui, carrément la supprimer si elle sert à rien (CàD si elle n'aide pas les gens) et la fusionner doucement aux journaux, en créant une section spéciale pour ça, au moins ça c'est pas cher comme solution, oui le mot est laché, je pense qu'il y a un probleme.

    Pour ce qui est du journal (désolé pour ce squat de commentaire) j'ai une question : pourquoi ne pas mettre directement le serveur face au monde? Un reverse proxy, SJNMA (Si Jeune, et déjà Mabuse) c'est pour permettre à un serveur (genre Apache ou Nginx) de "passer la main à / laisser passer les requetes vers" un autre serveur (typiquement Python, Node, etc.) non ? Si c'est bien le cas, alors donc ma question : Pourquoi ne pas sortir ce second serveur du domaine du premier, et servir directement ? Une réponse possible pourrait être de centraliser tous les "seconds" serveurs dans la config (typiquement sécu) du "premier" serveur…. J'ai bon ? Enfin bref, super journal, devrait être une entrée de forum si les forums étaient encore "un truc" mais donc voilà ;)

    • [^] # Re: Forums

      Posté par  . Évalué à 5 (+5/-0).

      Un reverse proxy, SJNMA (Si Jeune, et déjà Mabuse) c'est pour permettre à un serveur (genre Apache ou Nginx) de "passer la main à / laisser passer les requetes vers" un autre serveur (typiquement Python, Node, etc.) non ?

      Pas que. En l'occurrence, Traefik, HAProxy, Pound, et certains modules Apache comme mod_proxy_balancer etc… font de la répartition de charge, et savent surveiller les noeuds qui tombent en rade pour les enlever/mettre dynamiquement du pool. Et bien plus encore.

      L'autre intérêt d'avoir un reverse proxy est d'accéder à des fonctions qui ne sont pas forcément dans le serveur applicatif, comme la réécriture d'URLs, l'authentification, les protections DDoS et j'en passe (voir la liste des modules Apache par exemple).

      Aussi, le reverse proxy va servir à faire une rupture protocolaire, qui va (souvent) permettre de faire un filtrage plus fin entre des zones de sécurité, et limiter l'impact des bugs présents dans le serveur d'appli, qui souvent est plus complexe que le proxy, et à accès à plus de choses (genre base de données).

      Enfin, on voit beaucoup de reverse proxies mutualisés entre services : par exemple, au lieu de mettre sur le grand net sauvage tous tes serveurs, tu n'exposes que les proxies (typiquement une paire en HA).

      Tout n'est pas rose, bien sûr : si tes reverse proxies mutualisés plantent, toutes les applis derrière deviennent indisponibles.

      Bref, c'est bien plus qu'un passe plat ;)

  • # Nginx

    Posté par  . Évalué à 10 (+17/-0).

    Tu n'as pas mentionné Nginx qui est quand même le plus connu, pourquoi ?

    • [^] # Re: Nginx

      Posté par  . Évalué à 3 (+2/-0).

      Nginx aussi démarre en root. Et c'est plutôt normal sous Linux vu que c'est un prérequis (la capability CAP_NET_BIND_SERVICE), par défaut, pour avoir accès au port 80.

      • [^] # Re: Nginx

        Posté par  . Évalué à 4 (+3/-0).

        Il démarre en root mais drop ses privilèges par la suite. À noter qu'avec systemd, c'est facile de donner la capability à un process non privilégié (j'ai pas testé avec nginx, mais avec d'autres services)

        Sinon, moi c'est nginx que je recommande. Je ne sais pas si c'est le meilleur, mais il est très performant, sa configuration est assez souple, bref, pour la plupart des cas d'usage, il convient parfaitement

      • [^] # Re: Nginx

        Posté par  . Évalué à 2 (+0/-0).

        Il n'est pas obligé de tourner sous root. Je n'ai jamais regardé comment faire en ipv6 mais en ipv4 tu peux rediriger le port 80 vers autre chose.

        Du reste dans un contexte de conteneurs, le projet nginx upstream distribue des dockerfiles et image pour faire tourner nginx en tant que user nginx et écoutant par défaut sur le port 8080 : https://hub.docker.com/r/nginxinc/nginx-unprivileged

        • [^] # Re: Nginx

          Posté par  (site Web personnel) . Évalué à 2 (+0/-0). Dernière modification le 05/03/21 à 21:25.

          Il n'est pas obligé de tourner sous root. Je n'ai jamais regardé comment faire en ipv6 mais en ipv4 tu peux rediriger le port 80 vers autre chose.

          À noter que c’est pas juste une « redirection » (du NAT par exemple), c’est le process docker-proxy qui écoute sur le port qui agit comme un proxy.

      • [^] # Re: Nginx

        Posté par  . Évalué à 3 (+1/-0). Dernière modification le 06/03/21 à 12:27.

        c'est un prérequis […] pour avoir accès au port 80.

        Perso, sur mon serveur, j'ai dans /etc/runit/2 (entres autres):

        iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 80  -j REDIRECT --to-port 8080
        iptables -t nat -A PREROUTING -i ens3 -p tcp --dport 443 -j REDIRECT --to-port 8443
        

        Puis dans le sv/httpd/run:

        #!/bin/sh
        
        . /etc/runit/common
        exec ./darkhttpd /home/user/www
        

        Et: sv/httpsd/run:

        #!/bin/sh
        
        . /etc/runit/common
        
        exec hitch --log-level=2 '--frontend=[51.178.51.166]:8443' '--backend=[51.178.51.166]:8080' https.pem
        

        Bref, je vois pas en quoi c'est un problème, de pas être root. Bon, ce serveur est un jouet mal géré, faut que je nettoie, mais quand même, pas grand chose n'a ma confiance pour être lancé avec les droits root dessus.

        Je sais, les processus peuvent drop les droits, en théorie. Sauf que bon, même si j'essaie d'aller lire le code un minimum, je trouve tellement plus simple qu'il n'aient pas à le faire eux-mêmes, ça m'évite d'aller lire le code en détails.

        Non, ça ne répond pas à la question du forumW journal, certes, mais bon, ça répon au "besoin d'être root" par l'exemple.

      • [^] # Démarrer nginx en utilisateur non privilégié avec systemd

        Posté par  (site Web personnel) . Évalué à 3 (+2/-0).

        Nginx aussi démarre en root. Et c'est plutôt normal sous Linux vu que c'est un prérequis (la capability CAP_NET_BIND_SERVICE), par défaut, pour avoir accès au port 80.

        Ça n'est pas obligé. Il y a un hack non documenté qui permet de faire de l'activation par socket avec systemd et donc de le faire tourner sous utilisateur non privilégié.

        Mon fichier nginx.socket :

        [Unit]
        PartOf=nginx.service
        [Socket]
        # fd: 3
        ListenStream=80
        # fd: 4
        ListenStream=0.0.0.0:80
        # fd: 5
        ListenStream=443
        # fd: 6
        ListenStream=0.0.0.0:443
        BindIPv6Only=ipv6-only
        

        Mon fichier nginx.service :

        # On a besoin de nginx.socket pour ouvrir les ports et les envoyer à nginx
        [Unit]
        After=nginx.socket
        Requires=nginx.socket
        [Service]
        PIDFile=/run/nginx/pid
        ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
        ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx/pid
        TimeoutStopSec=5
        
        Environment=NGINX=3:4:5:6:
        NonBlocking=true
        
        User=www-data
        # Puis plein d'options de confinement avec systemd : NoNewPrivileges=true, CapabilityBoundingSet=, ...
        

        Puis le fichier nginx.conf :

        http {
                server {
                    # Les directives listen ci-dessous sont assez fictives : en réalité, nginx va écouter
                    # sur des "file descriptor" (fd) passés en paramètre (cf. /etc/systemd/system/nginx.socket)
                    listen [::]:80 default_server; # fd: 3
                    listen 80 default_server;      # fd: 4
                    #[...]
                }
                server {
                    listen [::]:443 ssl default_server; # fd: 5
                    listen 443 ssl default_server;      # fd: 6
                    #[...]
                }
        }
        

        Le seul truc qu'on perd, c'est systemctl reload nginx : ça n'est pas possible car dans ce cas le nginx oublie les "file descriptors" en entrée et tente d'attraper les ports, il ne peut pas. Il faut donc faire restart à chaque fois.

      • [^] # Re: Nginx

        Posté par  (site Web personnel) . Évalué à 1 (+0/-0).

        Tu peux binder sur un port < 1024 et démarrer pas en root, si tu mets les bons attributs sur le binaire nginx.

  • # haproxy

    Posté par  . Évalué à 5 (+5/-0).

    Tu peux regarder du côté de haproxy et y associer consul + consul template. Haproxy a des API donc je pense que tu peux automatiser pas mal de choses.

  • # Docker en root

    Posté par  (site Web personnel) . Évalué à 5 (+3/-0). Dernière modification le 05/03/21 à 15:50.

    L'image de conteneur officielle exécute Traefik avec l'utilisateur ROOT

    La gestion des utilisateurs dans Docker c’est franchement n’importe quoi et je comprendrais jamais le principe de la directive USER.

    En tant qu’admin sys, c’est à moi de choisir quel UID je donne à un process, pas au développeur. Le développeur devrait faire en sorte que son application puisse tourner avec -u{mathjax} (id -u nobody):(id -g nobody) (et aussi --read-only, mais c’est un autre sujet) et c’est tout.


    Le markdown sur DLFP a l’air de pas aimer $().

    • [^] # Re: Docker en root

      Posté par  (site Web personnel) . Évalué à 2 (+0/-0).

      Le markdown sur DLFP a l’air de pas aimer $().

      Il y a une entrée de suivi.

      Ce que je voulais écrire c’est ça :

      -u $(id -u nobody):$(id -g nobody)
      
    • [^] # Re: Docker en root

      Posté par  . Évalué à 3 (+2/-0).

      Tout à fait, et c'est d'ailleurs un pré-requis d'openshift par exemple, l'image démarre avec un user anonyme. Et c'est ce que j'ai appliqué pendant des années. On peut jouer sur les groupes, les contextes selinux, etc. pour faire ça.

      On peut construire des images agnostiques mais c'est difficile. Et c'est dû au fait qu'une image est tout simplement un filesystem, avec ses droits, comme sur un fs "normal" de serveur. D'où le USER, d'ailleurs.

  • # Pas envie de root ? Change d'user...

    Posté par  . Évalué à 4 (+2/-0).

    docker run --help me donne ceci :

    -u, --user string Username or UID (format: <name|uid>[:<group|gid>])

    Alors, c'est pas la panacée, faut que l'image soit prévue pour tourner avec n'importe quel utilisateur, mais dans la plupart des cas ça fonctionne sans soucis (ou avec un volume à mettre au bon endroit).

    Le seul qui m'a vraiment posé problème jusqu'à présent, c'est elasticsearch avec sa création de clé dans le dossier config…

  • # Nginx Unit

    Posté par  . Évalué à 2 (+2/-0).

    Encore assez récent mais qui semble prometteur : https://unit.nginx.org/

    • [^] # Re: Nginx Unit

      Posté par  (site Web personnel) . Évalué à 2 (+0/-0).

      Pour info, c’est notamment utilisé par les développeur de l’image Docker de netbox.

      Avant ils avaient nginx et gunicorn dans le même containers, avec supervisord pour gérer tout ça, et ils viennent de passer à unit. D’un point de vue utilisateur, j’ai vu aucune différence notable (à part les logs un peu différent).

  • # A part

    Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

    À part le fait d'avoir besoin d'une image qui ne tourne pas en root, Apache httpd + mod_md coche beaucoup de cases.

    Le seul souci que j'ai avec, c'est de devoir relancer httpd quand tu as un nouveau certificat (y compris le certificat initial).

    J'ai pas encore trouvé de solution élégante qui me plaise (et qui reste dans ma contrainte de ne pas rajouter trop d'étape sur mon déploiement ansible).

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.