Wie kann ich HAproxy mit SSL verwenden und X-Forwarded-For-Header abrufen UND PHP mitteilen, dass SSL verwendet wird?

20

Ich habe folgendes Setup:

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

Bei HTTP-Anfragen funktioniert dies hervorragend . Anfragen werden einwandfrei an meine Apache-Server verteilt. Bei SSL-Anforderungen verteilte HAproxy die Anforderungen mithilfe des TCP-Lastenausgleichs. Dies funktionierte jedoch, da HAproxy nicht als Proxy fungierte, den X-Forwarded-ForHTTP-Header nicht hinzufügte und die Apache / PHP-Server die Clients nicht kannten echte IP-Adresse.

Also fügte ich stunnelvor HAproxy hinzu und las, dass stunnel den X-Forwarded-ForHTTP-Header hinzufügen könnte . Das Paket, das ich in pfSense installieren konnte, fügt diesen Header jedoch nicht hinzu. Dies beeinträchtigt anscheinend meine Fähigkeit, KeepAlive-Anforderungen zu verwenden , die ich wirklich gerne behalten würde. Das größte Problem, das diese Idee tötete, war jedoch, dass stunnel die HTTPS-Anforderungen in einfache HTTP-Anforderungen umwandelte, sodass PHP nicht wusste, dass SSL aktiviert war, und versuchte, auf die SSL-Site umzuleiten.

Wie kann ich HAproxy zum Lastenausgleich für mehrere SSL-Server verwenden, sodass diese Server sowohl die IP-Adresse des Clients als auch wissen, dass SSL verwendet wird? Und wenn möglich, wie mache ich das auf meinem pfSense-Server?

Oder sollte ich das alles fallen lassen und einfach Nginx verwenden?

Josh
quelle
3
Re: stunnel und X-Forwarded-For, siehe hier .
Shane Madden
@ Shane: Danke. Genau dort habe ich gelesen, dass ich KeepAlive verliere :-)
Josh
2
+1 für hervorragende ASCII-Diagramme. :-)
KyleFarris
@ Alanhamlett, Ihr Link ist 404.
luckydonald
@luckydonald danke, hier ist ein aktualisierter Link. Sie können das Proxy-Protokoll verwenden, indem Sie das Schlüsselwort send-proxy zu Ihrer Haproxy-Konfiguration hinzufügen. Ich schrieb einen Blog-Beitrag mit Beispielen hier: wakatime.com/blog/23-how-to-scale-ssl-with-haproxy-and-nginx
Alan Hamlett

Antworten:

17

Sie brauchen nicht alles fallen zu lassen, Sie könnten einfach nginx vor haproxy für die SSL-Unterstützung verwenden und Ihre gesamte Konfiguration für den Lastenausgleich beibehalten. Sie müssen nicht einmal Nginx für HTTP verwenden, wenn Sie nicht möchten. Nginx kann sowohl X-Forwarded-For als auch einen benutzerdefinierten Header übergeben, der angibt, dass SSL verwendet wird (und Client-Zertifikatsinformationen, wenn Sie dies wünschen). Nginx-Konfigurations-Snippet, das die erforderlichen Informationen sendet:

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
quelle
37

Nur zur Veranschaulichung, da dieser Thread häufig für HAProxy + SSL verwendet wird, unterstützt HAProxy seit 1.5-dev12 auf beiden Seiten natives SSL. X-Forwarded-For, HTTP-Keep-Alive und ein Header, der dem Server mitteilt, dass die Verbindung über SSL hergestellt wurde, sind also so einfach wie folgt:

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

Ich bin mir sicher, dass Sie zu dem Zeitpunkt, als Sie sich etwas anderes einfallen ließen, zumindest neue Besucher die einfache Lösung finden werden :-)

Willy Tarreau
quelle
Danke, das ist eine gute allgemeine Information ... meine Frage betraf HAproxy, das unter pfSense ausgeführt wird. Daher muss ich vorerst noch nginx vor HAproxy verwenden, da pfSense diese Version von HAProxy (noch) nicht unterstützt.
Josh
Entschuldigung Josh, ich weiß nicht genug über pfSense, um zu wissen, ob Sie Komponenten aktualisieren können oder nicht, und da Sie über die Installation eines Pakets gesprochen haben, habe ich geglaubt, dass dies der Fall ist. Das letzte Mal habe ich es vor ungefähr 5 Jahren versucht, daher kann ich mich nicht an alle Details erinnern.
Willy Tarreau
1
Im Moment verstehe ich nicht viel über die Haproxy-Konfiguration, aber mit der neuesten Version musste ich ein acl: hinzufügen acl is-ssl dst_port 443und eine Zeile neu schreiben: reqadd X-Forwarded-Proto:\ https if is-sslNginx scheint mit diesem Header ziemlich gut
umzugehen
Das hat wie ein Zauber gewirkt. Kein Nginx erforderlich.
Jay Taylor
1
@ greg0ire das liegt daran, dass es mit dem neuesten haproxy kein is_ssl gibt, sondern stattdessen
ssl_fc
12

Für alle anderen, die diese Frage finden, bin ich Ochotos Rat gefolgt und habe Nginx verwendet. Hier sind die spezifischen Schritte, mit denen ich diese Funktion auf meinem pfSense-Router ausgeführt habe :

  1. Unter Verwendung der pfsense-Weboberfläche habe ich das Paket pfsense PfJailctl und das Paket "jail_template" unter System> Packages installiert, damit ich ein FreeBSD- Jail erstellen kann, unter dem nginx auf dem pfsense-System kompiliert und installiert werden kann.

  2. Ich habe ein Jail für meinen Nginx-Server unter " Dienste"> "Jails" konfiguriert und dem neuen Jail denselben Hostnamen und dieselbe IP-Adresse wie dem virtuellen IP-Alias ​​zugewiesen, auf dem HAproxy ausgeführt wurde. Ich habe das Gefängnis an die WAN-Schnittstelle gebunden. Ich habe die Standardvorlage für Gefängnisse verwendet und unionfs anstelle von nullfs aktiviert.

  3. Sobald das Gefängnis angefangen hatte, ging ich in die Pfsense-Box und rannte jls, um die Nummer des Gefängnisses zu finden. Dann rannte ich los jexec 1 sh, um eine Granate ins Gefängnis zu bringen. Von dort aus habe ich BSD-Ports eingerichtet und nginx installiert mit:

    portsnap extract
    portsnap fetch update
    cd /usr/ports/www/nginx
    make install clean
    
  4. Ich habe dann nginx so konfiguriert, dass es auf Port 443 lauscht und alle Anfragen an HAproxy über Port 80 weiterleitet, einschließlich der realen IP und des SSL-Status in HTTP-Headern. Meine usr/local/etc/nginx/nginx.confsieht aus wie:

    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. Ich habe dann meine PHP-Anwendung geändert, um den X-Forwarded-ProtoHTTP-Header zu erkennen :

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

Das endgültige Setup lautet also:

(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
                [    (pfSense server)  ]
Josh
quelle
2
Sie sollten SSLv2 deaktivieren, sofern Sie es nicht wirklich benötigen. gnu.org/software/gnutls/manual/html_node/… Ich weiß nicht, warum Nginx es in seiner Standardkonfiguration noch unterstützt.
Ochoto
Beachten Sie auch, dass Sie mit 1024 Worker-Verbindungen höchstens 512 gleichzeitige Clients unterstützen.
Ochoto
@Ochoto: Danke für diese beiden Tipps! Ich bin neu in HAproxy, aber noch weniger vertraut mit Nignx ...
Josh
7

Meine Konfiguration für eine 1.5-dev-17-Version von 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

Es verwendet die ssl_fcACL. Bitte beachten Sie, dass das option http-server-closeTeil sehr wichtig ist.

greg0ire
quelle
Vielen Dank! Ich verwende HAProxy v1.4, deshalb glaube ich nicht, dass ich das kann, aber es könnte anderen helfen.
Josh
Ja, und die 1.5 sollte bald herauskommen.
Greg0ire
5

HAProxy kann ein SSL-Backend nicht erreichen, ohne den rohen TCP-Modus zu verwenden. X-Forwarded-ForSie können jedoch möglicherweise den Datenverkehr mit einem Überwachungsstunnel für den Backend-Transit neu verschlüsseln. Hässlich.

Ich mag den Ansatz von Ochoto besser, mit einer Einschränkung: Nginx ist ein perfekter Load Balancer. Wenn Sie es verwenden, würde ich sagen, verwenden Sie es für alles. Übertragen Sie eingehendes HTTPS per Proxy, um ausgeglichene HTTPS-Backends zu laden. Auf diese Weise sind keine benutzerdefinierten Header für SSL-Informationen erforderlich (es sei denn, Sie benötigen das Client-Zertifikat).

Shane Madden
quelle
Ich bin nicht sicher, warum ich mich an HAproxy klammere. Ich denke, es liegt daran, dass pfSense ein Paket dafür hat und SOIS es verwendet. Keiner von beiden ist ein guter Grund. :-)
Josh
Ich schweife von Nginx ab, da es ein leistungsfähiger Load Balancer ist. Sofern Sie nicht das Nicht-Standard-Modul upstream_fair verwenden, führt es ein einfaches Round-Robin (oder Client-IP-Hash) durch, ohne zu berücksichtigen, ob das Ziel-Backend bereits mit Anfragen beschäftigt ist und die Warteschlange in diesem Backend dann wächst Andere Backends sind kostenlos und warten auf einen Job. HAProxy überwacht auch Backends sehr gut und zeigt Statistiken dazu an.
Ochoto
Wenn nur eine der folgenden Aussagen zutrifft: a) Nginx erhält eine anständige Zustandsüberwachung und einen fairen Lastausgleich. B) HAProxy erhält eine anständige SSL-Unterstützung. Man kann nur hoffen
Yavor Shahpasov,
Ich habe gerade ein Setup mit nginx -> haproxy -> nginx -> backend für SSL implementiert. Dies liegt an der mangelnden HTTPS-Unterstützung in haproxy, wie hier beschrieben, aber auch daran, dass nginx keine http-Health-Check-Skripte unterstützt.
Geoffrey
2

Ich habe letztes Jahr eine Lösung implementiert, um HAProxy so in pfSense zu integrieren , dass alle Funktionen von HAProxy genutzt werden und eine gute Isolation mit pfSense besteht. Damit ist es eine praktikable Option für Produktionsumgebungen . SSL wird auf HAProxy beendet . Ich habe HAProxy mit ezjail und Ports Collection in einem Jail in pfSense installiert . Auf diese Weise ist es sehr einfach, beide Komponenten unabhängig voneinander zu warten. Und Sie können jede gewünschte Version installieren. Ich habe mit 1.5-dev13 angefangen. Und seitdem funktioniert es perfekt für mich. Ich habe das Ganze hier dokumentiert.

HAProxy unter pfSense installieren

Übrigens, Willy, vielen Dank für dieses hervorragende Produkt.

Dinesh Sharma
quelle