Comment puis-je utiliser HAproxy avec SSL et obtenir des en-têtes X-Forwarded-For ET dire à PHP que SSL est utilisé?

20

J'ai la configuration suivante:

(internet) ---> [  pfSense Box  ]    /-> [ Apache / PHP server ]
                [running HAproxy] --+--> [ Apache / PHP server ]
                                    +--> [ Apache / PHP server ]
                                     \-> [ Apache / PHP server ]

Pour les requêtes HTTP, cela fonctionne très bien , les requêtes sont très bien distribuées sur mes serveurs Apache. Pour les demandes SSL, j'avais HAproxy distribuant les demandes à l'aide de l'équilibrage de charge TCP, et cela a fonctionné cependant puisque HAproxy n'a pas agi en tant que proxy, il n'a pas ajouté l' X-Forwarded-Foren-tête HTTP et les serveurs Apache / PHP ne connaissaient pas le client. véritable adresse IP.

Donc, j'ai ajouté stunneldevant HAproxy, en lisant que Stunnel pouvait ajouter l' X-Forwarded-Foren-tête HTTP. Cependant, le paquet que je pourrais installer dans pfSense n'ajoute pas cet en-tête ... aussi, cela tue apparemment ma capacité à utiliser les requêtes KeepAlive , que j'aimerais vraiment garder. Mais le plus gros problème qui a tué cette idée était que Stunnel convertissait les requêtes HTTPS en requêtes HTTP simples, donc PHP ne savait pas que SSL était activé et tentait de rediriger vers le site SSL.

Comment puis-je utiliser HAproxy pour équilibrer la charge sur un certain nombre de serveurs SSL, permettant à ces serveurs à la fois de connaître l'adresse IP du client et de savoir que SSL est utilisé? Et si possible, comment puis-je le faire sur mon serveur pfSense?

Ou devrais-je laisser tomber tout cela et utiliser simplement nginx?

Josh
la source
3
Re: stunnel et X-Forwarded-For, voir ici .
Shane Madden
@Shane: Merci. C'est exactement là que j'ai lu que je perdais KeepAlive :-)
Josh
2
+1 pour un excellent diagramme ASCII. :-)
KyleFarris
@AlanHamlett, votre lien est le 404.
luckydonald
@luckydonald merci, voici un lien mis à jour. Vous pouvez utiliser le protocole proxy en ajoutant le mot clé send-proxy à votre configuration haproxy. J'ai écrit un article de blog avec des exemples ici: wakatime.com/blog/23-how-to-scale-ssl-with-haproxy-and-nginx
Alan Hamlett

Réponses:

17

Vous n'avez pas besoin de tout laisser tomber, vous pouvez simplement utiliser nginx devant haproxy pour le support SSL, en gardant toute votre configuration d'équilibrage de charge. Vous n'avez même pas besoin d'utiliser nginx pour HTTP si vous ne le souhaitez pas. Nginx peut transmettre à la fois X-Forwarded-For et un en-tête personnalisé indiquant que SSL est utilisé (et les informations de certificat client si vous le souhaitez). Extrait de configuration Nginx qui envoie les informations requises:

proxy_set_header SCHEME $scheme;      # http/https
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header CLIENT_CERT $ssl_client_raw_cert;
Ochoto
la source
37

Pour mémoire, comme ce fil est souvent mentionné concernant HAProxy + SSL, HAProxy prend en charge le SSL natif des deux côtés depuis 1.5-dev12. Donc, avoir X-Forwarded-For, HTTP keep-alive ainsi qu'un en-tête indiquant au serveur que la connexion a été établie via SSL est aussi simple que ce qui suit:

listen front
    bind :80
    bind :443 ssl crt /etc/haproxy/haproxy.pem
    mode http
    option http-server-close
    option forwardfor
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    server srv1 1.1.1.1:80 check ...
    ...

Je suis sûr qu'au moment où vous avez trouvé quelque chose de différent, mais au moins les nouveaux visiteurs auront la solution facile maintenant :-)

Willy Tarreau
la source
Merci, c'est une bonne information générale ... ma question concernait HAproxy fonctionnant sur pfSense donc pour l'instant je dois encore utiliser nginx devant HAproxy, car pfSense ne supporte pas (encore) cette version de HAProxy
Josh
Désolé Josh, je ne connais pas assez pfSense pour savoir si vous pouvez mettre à jour des composants dessus ou non, et puisque vous parliez d'installer un paquet, je pensais que c'était le cas. La dernière fois que j'ai essayé, c'était il y a environ 5 ans, donc je ne me souviens pas de tous les détails.
Willy Tarreau
1
Pour l'instant, je ne comprends pas grand chose à la configuration haproxy, mais avec la dernière version, j'ai dû ajouter un acl: acl is-ssl dst_port 443et réécrire une ligne: reqadd X-Forwarded-Proto:\ https if is-sslNginx semble gérer cet en-tête assez bien
greg0ire
Cela a fonctionné comme un charme. Aucun nginx requis.
Jay Taylor
1
@ greg0ire c'est parce qu'avec le dernier haproxy il n'y a pas is_ssl mais ssl_fc à la place
josch
12

Pour tous ceux qui trouvent cette question, j'ai suivi les conseils d'Ochoto et utilisé nginx. Voici les étapes spécifiques que j'ai utilisées pour que cela fonctionne sur mon routeur pfSense :

  1. À l'aide de l'interface Web pfsense, j'ai installé le package pfsense PfJailctl et le package "jail_template" sous Système> Packages afin de pouvoir créer une prison FreeBSD sous laquelle compiler et installer nginx sur le système pfsense.

  2. J'ai configuré une prison pour mon serveur nginx sous Services> Jails , donnant à la nouvelle prison le même nom d'hôte et la même adresse IP que l'alias IP virtuel sur lequel j'avais HAproxy en cours d'exécution. J'ai lié la prison à l'interface WAN. J'ai utilisé le modèle de prison par défaut et activé unionfs plutôt que nullfs.

  3. Une fois la prison commencée, j'ai SSHed dans la boîte pfsense et j'ai couru jlspour trouver le numéro de la prison. J'ai ensuite couru jexec 1 shchercher un obus à l'intérieur de la prison. De là, j'ai configuré les ports BSD et installé nginx en utilisant:

    portsnap extract
    portsnap fetch update
    cd /usr/ports/www/nginx
    make install clean
    
  4. J'ai ensuite configuré nginx pour écouter sur le port 443 et transmettre toutes les demandes à HAproxy sur le port 80, y compris la véritable IP et l'état SSL dans les en-têtes HTTP. Mes usr/local/etc/nginx/nginx.conflooks:

    worker_processes  1;
    
    events {
        worker_connections  2048;
    }
    
    http {
        upstream haproxy {
            server 209.59.186.35:80;
        }
    
        server {
            listen       443;
            server_name  my.host.name default_server;
            ssl                  on;
            ssl_certificate      my.crt;
            ssl_certificate_key  my.key;
            ssl_session_timeout  5m;
    
            ssl_protocols  SSLv3 TLSv1;
            ssl_ciphers  HIGH:!aNULL:!MD5;
            ssl_prefer_server_ciphers   on;
    
            location / {
                proxy_pass http://haproxy;
    
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
                proxy_set_header X-Forwarded-Proto https;
            }
        }
    
    }
    
  5. J'ai ensuite modifié mon application PHP pour détecter l' X-Forwarded-Protoen-tête HTTP:

    function usingSSL()
    {
        return (
           (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' )
            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
                   && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' ));
    }
    

La configuration finale est donc:

(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
                [    (pfSense server)  ]
Josh
la source
2
Vous devez désactiver SSLv2 sauf si vous en avez vraiment besoin. gnu.org/software/gnutls/manual/html_node/… Je ne sais pas pourquoi Nginx le supporte toujours dans sa configuration par défaut.
Ochoto
Sachez également qu'avec 1024 connexions de travail, vous prendrez en charge au plus 512 clients simultanés.
Ochoto
@Ochoto: Merci pour ces deux conseils! Je suis nouveau sur HAproxy mais encore moins familier avec nignx ...
Josh
7

Ma configuration pour une version 1.5-dev-17 de haproxy:

global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        #chroot /usr/share/haproxy
        user haproxy
        group haproxy
        daemon
        #debug
        #quiet

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option  http-server-close
        retries 3
        option redispatch
        fullconn 1000        
        maxconn 1000
        timeout queue 600s
        timeout connect 5s
        timeout client 600s
        timeout server 600s

frontend http-in
        bind *:80
        bind *:443 ssl crt /usr/local/etc/ssl/certs
        reqadd X-Forwarded-Proto:\ https if { ssl_fc }
        default_backend varnish-ha
        option forwardfor
backend varnish-ha
  server hafront1 10.1.69.1:6081  minconn 100 maxqueue 10000

Il utilise l' ssl_fcACL. Veuillez noter que la option http-server-closepartie est très importante.

greg0ire
la source
Merci! J'utilise HAProxy v1.4, donc je ne pense pas pouvoir le faire, mais cela pourrait aider les autres.
Josh
Oui, et le 1.5 devrait bientôt sortir.
greg0ire
5

HAProxy ne peut pas frapper un backend SSL sans utiliser le mode TCP brut, perdant X-Forwarded-For, mais vous pourriez potentiellement rechiffrer le trafic avec un stunnel d'écoute pour le transit du backend. Moche, cependant.

J'aime mieux l'approche d'Ochoto, avec une mise en garde: nginx est un équilibreur de charge parfaitement capable; si vous l'utilisez, je dirais l'utiliser pour tout. Proxy votre HTTPS entrant pour charger des backends HTTPS équilibrés - et de cette façon, il n'y a pas besoin d'en-têtes personnalisés pour les informations SSL (sauf si vous avez besoin du certificat client).

Shane Madden
la source
Je ne sais pas pourquoi je m'accroche à HAproxy. Je pense que c'est parce que pfSense a un package pour cela, et SOIS l'utilise. Ni l'un ni l'autre n'est une bonne raison. :-)
Josh
Je m'éloigne de nginx en étant un équilibreur de charge capable, à moins que vous n'utilisiez le module non standard upstream_fair, il fait un simple tour de rôle (ou hachage IP client) sans prendre en compte si le backend de destination est déjà occupé par des demandes et donc augmente la file d'attente dans ce backend lorsque il existe d'autres backends gratuits et en attente d'un emploi. HAProxy surveille également bien les backends et affiche des statistiques à leur sujet.
Ochoto
Si un seul des éléments suivants devenait vrai a) Nginx obtient un suivi d'état décent et un équilibrage de charge équitable b) HAProxy obtient un support SSL décent On ne peut qu'espérer
Yavor Shahpasov
Je viens de déployer une configuration utilisant nginx -> haproxy -> nginx -> backend pour SSL, cela est dû au manque de prise en charge HTTPS dans haproxy comme discuté ici, mais aussi parce que nginx ne prend pas en charge les scripts de contrôle de santé http.
Geoffrey
2

J'ai implémenté une solution l'année dernière pour intégrer HAProxy à pfSense de manière à exploiter toutes les fonctionnalités de HAProxy et à maintenir une bonne isolation avec pfSense. C'est donc une option viable pour les environnements de production . SSL est résilié sur HAProxy . J'ai installé HAProxy dans une prison dans pfSense en utilisant ezjail et Ports Collection . De cette façon, il est très facile de maintenir les deux composants indépendamment. Et vous pouvez installer la version que vous souhaitez. J'ai commencé avec 1.5-dev13. Et depuis, cela fonctionne parfaitement pour moi. J'ai documenté le tout ici.

Installer HAProxy sur pfSense

BTW Willy, merci beaucoup pour cet excellent produit.

Dinesh Sharma
la source