10

I have HAProxy for my two sites, one of them public and one private.

www.mysite.com private.mysite.com

Atm, I'm using haproxy like this:

frontend mysite_https bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3 mode http acl domain_www hdr_beg(host) -i www. acl domain_private hdr_beg(host) -i private. acl path_ghost path_beg /ghost/ acl clientcert ssl_c_used redirect location https://www.example.com if path_ghost !clientcert redirect location https://www.example.com if !domain_www !clientcert use_backend bknd_private if domain_private use_backend bknd_www if domain_www default_backend bknd_www 

What this should do is ask for a client certificate (optionally) and proceed. If the domain is not www.example.com and the visitor cannot provide the right certificate or the path is /ghost/ and the visitor cannot provide the right certificate, it should be redirected to https://www.example.com

So far, this works fine. However, I got complaints by Mac users browsing my site with Safari that they keep getting asked for the certificate when there browsing on https://www.example.com/ whereas for example Firefox only asks when browsing https://private.example.com/ or https://www.example.com/ghost/.

Appearently that's just how Safari works so I can't fix that. My idea was to use SNI to divide between different frontends

frontend mysite_https bind *.443 ssl crt /etc/mycert.pem no-sslv3 frontend private_https bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3 

Of course that doesn't work because

a. I can't have two frontends listening on port 443 with only one public IP b. I haven't found a way yet to say "use_frontend if domain_www" or something like that. (Only use_backend or use-server)

I also tried doing it with three haproxy servers

frontend haproxy-sni bind *:443 ssl crt /etc/mycert.pem no-sslv3 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req.ssl_hello_type 1 } acl domain_www ssl_fc_sni_end -i www.example.com use-server server1 haproxy-private.lan if !domain_www use-server server2 haproxy-public.lan if domain_www 

This works, the problem here however is that haproxy-private asks for the client certificate, but the request doesn't reach the browser. Somehow haproxy-sni drops the request.

Also, I now have three haproxy servers which is not desirable (although a possible option if I can't find a better solution).

Preferably I would like something like this (made up.. don't know the real options)

frontend mysite_https bind *.443 ssl crt /etc/mycert.pem no-sslv3 mode http acl domain_www hdr_beg(host) -i www. acl domain_private hdr_beg(host) -i private. acl path_ghost path_beg /ghost/ ssl_options ca-file /etc/myca.pem verify optional if !www_domain # made up! ssl_options ca-file /etc/myca.pem verify optional if !path_ghost # made up! acl clientcert ssl_c_used redirect location https://www.example.com if path_ghost !clientcert redirect location https://www.example.com if !domain_www !clientcert ... 

I hope someone can help me with this...

2 Answers 2

13

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.

3
  • Good idea. I could not find documentation on this setup either. Is performance similar with this HAProxy loop ? Commented Aug 24, 2015 at 9:25
  • Sry, i don't know how performant it is because A: didn't use it for long (because of source ip filters), B: don't have a hight traffic site, where performance optimization would be more interesting... Commented Aug 24, 2015 at 16:33
  • I just put apache2 in front of haproxy, which works but is sort of stupid because single-point-of-failure in front of hapeoxy cluster and (i think) performance bottleneck (i think hap is faster than ap2, got no real data on that though.) Commented Aug 24, 2015 at 16:41
6

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!

1
  • OP is using the same cert for both. The question was more about different ca-file setting. Commented Apr 19, 2020 at 23:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.