HAProxy SSL roundrobin not working when SSL terminated and forwarded
I am using the following config to terminate SSL so that I can inspect the request, do URL rewriting, ACL etc, and then forward the SSL traffic back to my backend servers. However, I am not able to get sticky to work. I can get sticky to work when I just use "mode tcp" and do straight forward tcp routing, but sticky stops working as soon as I start terminating SSL at front-end.
Here's my config:
frontend https-forward
bind *:443 ssl crt /etc/haproxy/certs.d/combo.pem
option http-server-close
option forwardfor
reqadd X-Forwarded-Proto:\ https
reqadd X-Forwarded-Port:\ 443
capture request header Referrer len 64
capture request header Content-Length len 10
capture request header User-Agent len 64
# set HTTP Strict Transport Security (HTST) header
rspadd Strict-Transport-Security:\ max-age=15768000
# some ACLs and URL rewrites...
default_backend backstuff
backend backstuff
log 127.0.0.1 local2 notice
balance roundrobin
option ssl-hello-chk
stick-table type binary len 32 size 30k expire 30m
acl clienthello req_ssl_hello_type 1
acl serverhello rep_ssl_hello_type 2
tcp-request inspect-delay 5s
tcp-request content accept if clienthello
tcp-response content accept if serverhello
stick on payload_lv(43,1) if clienthello
stick store-response payload_lv(43,1) if serverhello
server PO1 10.35.59.160:443 ssl verify none maxconn 5000
server PO2 10.35.59.161:443 ssl verify none maxconn 5000
Solution 1:
The reason you can't stick on SSL Session ID like you are when using mode http
(the default unless you explicitly specify mode tcp
) is that you're trying to do it on certain bytes of the packet's payload, when in reality the packets have already been decoded and those offsets might contain totally random data.
You have two options here.
-
Stick based on source IP
If you're not opposed to sticking on the client's IP, instead of the SSL session ID like you're doing now, then you can change your config to look like this:
frontend https-forward bind *:443 ssl crt /etc/haproxy/certs.d/combo.pem mode http option http-server-close option forwardfor reqadd X-Forwarded-Proto:\ https reqadd X-Forwarded-Port:\ 443 capture request header Referrer len 64 capture request header Content-Length len 10 capture request header User-Agent len 64 # set HTTP Strict Transport Security (HTST) header rspadd Strict-Transport-Security:\ max-age=15768000 # some ACLs and URL rewrites... default_backend backstuff backend backstuff mode http log 127.0.0.1 local2 notice balance roundrobin option ssl-hello-chk stick-table type ip size 30k expire 30m stick on src server PO1 10.35.59.160:443 ssl verify none maxconn 5000 server PO2 10.35.59.161:443 ssl verify none maxconn 5000
The key changes are the the
stick-table
andstick on
lines, as well as the explicit use ofmode http
.As you've said, if many clients accessing your site are behind a NAT, they'll all end up on the same server, so it's not the smoothest distribution but it does work and provides the functionality you want.
-
Use the HAProxy-decoded SSL session ID
Here'd you'd have to leverage HAproxy's knowledge of the connection via
ssl_fc_session_id
(docs).ssl_fc_session_id : binary
Returns the SSL ID of the front connection when the incoming connection was made over an SSL/TLS transport layer. It is useful to stick a given client to a server. It is important to note that some browsers refresh their session ID every few minutes.In this case, you'd use the same config as I've provided above, but with the
stick-table
andstick
lines changed to:stick-table type binary len 32 size 30k expire 30m stick on ssl_fc_session_id
This is really most akin to what you're trying to achieve.