HAProxy with SNI and different SSL Settings
Solution 1:
I found a solution to this problem, that doesn't require additional servers or services. I'm not entirely sure if this doesn't spawn new problems though. For me it seems to work right now.
The way I did it, was to create a frontend for each domain that required different ssl settings. I then set the bind option of those frontends to high ports (these are not reachable from public!).
I created another frontend listening on port :443 to divide traffic based on SNI, and set the backend servers to 127.0.0.1:high-port.
This way, I created sort of a loop in haproxy
[incoming]->[haproxy:443]->[haproxy:7000]->[www.intern.lan]
[incoming]->[haproxy:443]->[haproxy:8000]->[private.intern.lan]
Here is the config part.
frontend frnd_snipt # Frontend_SNI-PassThrough (snipt)
bind *:443 # Do not use bind *:8443 ssl crt etc....!
option tcplog
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl subdomain_is_www req_ssl_sni -i www.example.com
acl subdomain_is_www req_ssl_sni -i example.com
acl subdomain_is_private req_ssl_sni -i private.example.com
use_backend bknd_snipt_private if subdomain_is_private
use_backend bknd_snipt_www if subdomain_is_www
backend bknd_snipt_www
mode tcp # tcp mode must match the frontend mode - already set as default in [global]
server snipt-www 127.0.0.1:7000 # run without "check", otherwise haproxy checks itself all the time!
backend bknd_snipt_private
mode tcp
server snipt-private 127.0.0.1:8000 # also, don't add "ssl" when in tcp mode. "ssl" is an http mode option (result in "NO-SRV" when set in tcp)
##### NORMAL HAPROXY PART #####
frontend www_example_com # this frontend can be in tcp or http mode...
bind *:7000 ssl crt /etc/mycert.pem no-sslv3 # www. frontend with normal https
mode http
option httplog
frontend private_example_com
bind *:8000 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3 # private. frontend with client certificate request.
mode http
option httplog
... # whatever you have in your frontend
If anyone has thoughts on this, or any idea why this could be a bad idea please let me know. It works, but I'm wondering why use_frontend isn't an option. Maybe because it's something that shouldn't be done for whatever reasons.
Solution 2:
recent versions of haproxy support a setting called crt-list
which allows you to specify different TLS settings based on the matched certificate
you can use it like this:
haproxy.conf:
frontend https
mode http
bind *:443 ssl crt-list /etc/haproxy/crt-list.conf ca-file ca.pem
use_backend test if { ssl_fc_sni -i test.area.example.org }
use_backend private if { ssl_fc_sni -i private.example.org }
default_backend www
crt-list.conf:
www.pem [verify none]
www.pem [verify required] *.area.example.org
private.pem [verify required]
more info: https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#5.1-crt-list
note on security: always match your (sensitive) hostnames against SNI ssl_fc_sni
, not the HTTP hostname. Otherwise an attacker could possibly bypass your client cert auth by sending the TLS SNI of www.example.org
but set the HTTP hostname to private.example.org
!