如何将 HAproxy 与 SSL 一起使用并获取 X-Forwarded-For 标头并告诉 PHP 正在使用 SSL?

Jos*_*osh 20 ssl load-balancing haproxy pfsense

我有以下设置:

(internet) ---> [  pfSense Box  ]    /-> [ Apache / PHP server ]
                [running HAproxy] --+--> [ Apache / PHP server ]
                                    +--> [ Apache / PHP server ]
                                     \-> [ Apache / PHP server ]
Run Code Online (Sandbox Code Playgroud)

对于 HTTP 请求,这很好用,请求被分发到我的 Apache 服务器就好了。对于 SSL 请求,我让 HAproxy 使用 TCP 负载平衡分发请求,但是它可以工作,因为 HAproxy 没有充当代理,它没有添加X-Forwarded-ForHTTP 标头,并且 Apache/PHP 服务器不知道客户端的真实IP地址。

所以,我stunnel在HAproxy前面加了,读到stunnel可以加X-Forwarded-ForHTTP头。但是,我可以安装到 pfSense 中的包没有添加这个标头......而且,这显然会扼杀我使用 KeepAlive requests 的能力,我真的很想保留它。但是扼杀了这个想法的最大问题是 stunnel 将 HTTPS 请求转换为普通的 HTTP 请求,所以 PHP 不知道 SSL 已启用并试图重定向到 SSL 站点。

如何使用 HAproxy 在多个 SSL 服务器之间进行负载平衡,让这些服务器既知道客户端的 IP 地址知道 SSL 正在使用中?如果可能的话,我如何在我的 pfSense 服务器上做到这一点?

还是我应该放弃这一切而只使用 nginx?

Wil*_*eau 39

只是为了记录,因为这个线程经常被提到关于 HAProxy + SSL,HAProxy 从 1.5-dev12 开始确实支持双方的原生 SSL。因此,拥有 X-Forwarded-For、HTTP keep-alive 以及告诉服务器连接是通过 SSL 建立的标头非常简单,如下所示:

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 ...
    ...
Run Code Online (Sandbox Code Playgroud)

我相信当你想出不同的东西时,但至少新访客现在会得到简单的解决方案:-)


Och*_*oto 16

你不需要全部放弃,你可以在 haproxy 前面使用 nginx 来支持 SSL,保持你所有的负载平衡配置。如果您不想,您甚至不需要将 nginx 用于 HTTP。Nginx 可以传递 X-Forwarded-For 和指示 SSL 正在使用的自定义标头(如果需要,还可以传递客户端证书信息)。发送所需信息的 Nginx 配置片段:

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;
Run Code Online (Sandbox Code Playgroud)


Jos*_*osh 12

对于发现这个问题的其他人,我遵循了 Ochoto 的建议并使用了 nginx。这是我用来在我的 pfSense 路由器上完成这项工作的具体步骤:

  1. 使用 pfsense Web 界面,我在System > Packages下安装了 pfsense PfJailctl 包和“jail_template”包,这样我就可以创建一个 FreeBSD jail,在该监狱下在 pfsense 系统上编译和安装 nginx。

  2. 我在Services > Jails下为我的 nginx 服务器配置了一个 jail ,为新的 jail 提供与运行 HAproxy 的虚拟 IP 别名相同的主机名和 IP 地址。我将监狱绑定到 WAN 接口。我使用了默认的 jail 模板并启用了 unionfs 而不是 nullfs。

  3. 监狱启动后,我通过 SSH 连接到 pfsense 框并跑去jls寻找监狱的号码。然后我跑去jexec 1 sh监狱里拿一个炮弹。从那里我设置了 BSD 端口并使用以下方法安装了 nginx:

    portsnap extract
    portsnap fetch update
    cd /usr/ports/www/nginx
    make install clean
    
    Run Code Online (Sandbox Code Playgroud)
  4. 然后我将 nginx 配置为侦听端口 443,并将所有请求传递到端口 80 上的 HAproxy,包括真实 IP 和 HTTP 标头中的 SSL 状态。我的usr/local/etc/nginx/nginx.conf样子:

    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;
            }
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 然后我修改了我的 PHP 应用程序以检测X-Forwarded-ProtoHTTP 标头:

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

所以最后的设置是:

(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
                [    (pfSense server)  ]
Run Code Online (Sandbox Code Playgroud)

  • 除非确实需要,否则应该禁用 SSLv2。http://www.gnu.org/software/gnutls/manual/html_node/On-SSL-2-and-older-protocols.html 我不知道为什么 Nginx 在其默认配置中仍然支持它。 (2认同)

gre*_*ire 7

我对 1.5-dev-17 版本的 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
Run Code Online (Sandbox Code Playgroud)

它使用ssl_fcACL。请注意,该option http-server-close部分非常重要。


Sha*_*den 5

HAProxy 不能在不使用原始 TCP 模式的情况下访问 SSL 后端,丢失X-Forwarded-For,但是,您可能会使用后端传输的侦听通道重新加密流量。不过丑。

我更喜欢 Ochoto 的方法,但有一个警告:nginx 是一个功能完善的负载均衡器;如果你正在使用它,我会说把它用于一切。将传入的 HTTPS 代理到负载平衡的 HTTPS 后端 - 这样,就不需要 SSL 信息的自定义标头(除非您确实需要客户端证书)。