How can I use HAproxy with SSL and get X-Forwarded-For headers AND tell PHP that SSL is in use?
Solution 1:
You dont need to drop it all, you could just use nginx in front of haproxy for SSL support, keeping all your load balancing config. You dont even need to use nginx for HTTP if you don't want to. Nginx can pass both X-Forwarded-For and a custom header indicating SSL is in use (and client cert information if you want). Nginx config snippet that sends required information:
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;
Solution 2:
Just for the record, as this thread is often referred to concerning HAProxy + SSL, HAProxy does support native SSL on both sides since 1.5-dev12. So having X-Forwarded-For, HTTP keep-alive as well as a header telling the server that the connection was made over SSL is as simple as the following :
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 ...
...
I'm sure that by the time you came up with something different, but at least new visitors will get the easy solution now :-)
Solution 3:
For anyone else who finds this question, I followed Ochoto's advice and used nginx. Here's the specific steps I used to make this work on my pfSense router:
Using the pfsense web interface, I installed the pfsense PfJailctl package and the "jail_template" package under System > Packages so I could create a FreeBSD jail under which to compile and install nginx on the pfsense system.
I configured a jail for my nginx server under Services > Jails, giving the new jail the same hostname and IP address of the virtual IP alias which I had HAproxy running on. I bound the jail to the WAN interface. I used the default jail template and enabled unionfs rather than nullfs.
-
Once the jail had been started, I SSHed in to the pfsense box and ran
jls
to find the jail's number. I then ranjexec 1 sh
to get a shell inside the jail. From there I set up BSD ports and installed nginx using:portsnap extract portsnap fetch update cd /usr/ports/www/nginx make install clean
-
I then configured nginx to listen on port 443, and pass all requests to HAproxy on port 80, including the real IP and the SSL status inside HTTP headers. My
usr/local/etc/nginx/nginx.conf
looks like: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; } } }
-
I then modified my PHP application to detect the
X-Forwarded-Proto
HTTP Header:function usingSSL() { return ( (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' )); }
So the final setup is:
(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
[ (pfSense server) ]
Solution 4:
My configuration for a 1.5-dev-17 version of 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
It uses the ssl_fc
ACL. Please note that the option http-server-close
part is very important.