6

how can I use ACL rules in haproxy (1.5dev19) for server multiple hosts with own ssl certificate for each??

I have 3 backends with multiple domains all on one IP address.

nodejs (http/https/ws/wss)

nginx (http/https) (reverse proxy for apache now)

apache (http)

I want to put haproxy before them and make it serve ssl connections. is it possible with haproxy acl or I have to use something like stud in front of haproxy?

something like:

frontend http-in
    bind *:80

    acl host_domain1 hdr(host) -i domain1.lt
    use_backend nginx_web_http if host_domain1

frontend http-in
    bind *:443

    acl host_domain1 hdr(host) -i domain1.lt
    use_backend nginx_web_https if host_domain1

backend nginx_web_https
    mode http
    ssl crt /etc/ssl/domain1/ crt ./certs/ prefer-server-cipher
    option httplog
    option httpclose
    server nginx 192.168.2.101:8080 check

backend nginx_web_http
    mode http
    option httplog
    option httpclose
    server nginx 192.168.2.101:8080 check
1
  • SNI is not supported by legacy browsers. Save the trouble and use 3 IPs with your haproxy.
    – wqw
    Dec 21, 2013 at 18:20

2 Answers 2

3

I don't think haproxy will allow you to specify a per-backend SSL certificate for each incoming request, rather you'd have to have a combined certificate that allows for multiple domain names (SNI).

Here's a guide on using SNI with haproxy, where all the certificates are actually hosted by the haproxy server, not the backend instances: https://trick77.com/haproxy-and-sni-based-ssl-offloading-with-intermediate-ca/

Also see the example at the end of this section: http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#4.2-use-server

8
  • SNI is good choice, but I want to use certificates that I already have. So i see 3 options. 1) use haproxy in tcp mode and server ssl from apache or nginx (i dont know if it's possible) 2) use nginx in front of haproxy, which is not really cool. 3) use another reverse proxy like pound which I don't know if can handle my requirements :D
    – zajca
    Dec 16, 2013 at 12:36
  • 1
    Option 1 will half-work, but you won't be able to decide which backend to send to from haproxy, since the packet is encrypted. Dec 16, 2013 at 16:43
  • From the article, it seems like you can place all your existing certs in /etc/haproxy/certs.d, and use them via HAPRoxy to decrypt the traffic, and then send to the correct backend. Dec 16, 2013 at 16:44
  • And how is this solve by apache? Because in apache i can use for each virtualhost different certificate. Is apache using SNI for this? As I'm reading articles all I here are possible problem but this is working fine on apache.
    – zajca
    Dec 17, 2013 at 7:58
  • I'm not 100% sure on Apache, but here's an article on how to do the same with nginx: digitalocean.com/community/articles/… It also uses SNI, as do most other multi-ssl certificate guides will. Dec 17, 2013 at 21:25
10

You can actually do this with your version of haproxy. I've blogged about it here

Here's the example:

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
   retries  3
   option redispatch
   maxconn  2000
   contimeout  5000
   clitimeout  50000
   srvtimeout  50000

# Host HA-Proxy web stats on Port 3306 (that will confuse those script kiddies)
listen HAProxy-Statistics *:3306
    mode http
    option httplog
    option httpclose
    stats enable
    stats uri /haproxy?stats
    stats refresh 20s
    stats show-node
    stats show-legends
    stats show-desc Workaround haproxy for SSL
    stats auth admin:ifIruledTheWorld
    stats admin if TRUE

frontend ssl_relay 192.168.128.21:443
    # this only works with 1.5 haproxy
    mode tcp
    option tcplog
    option socket-stats
    # option nolinger
    maxconn  300

    # use tcp content accepts to detects ssl client and server hello.
    # acl clienthello req_ssl_hello_type 1 -> seems to not work

    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }

    use_backend ssl_testdomain_prod if { req_ssl_sni -i www.testdomain.nl }
    use_backend ssl_testdomain_stag if { req_ssl_sni -i test.testdomain.nl }

    default_backend ssl_testdomain_stag

backend ssl_testdomain_stag
   mode tcp
   #option nolinger
   option tcplog
   balance roundrobin
   hash-type consistent
   option srvtcpka

    # maximum SSL session ID length is 32 bytes.
    stick-table type binary len 32 size 30k expire 30m

    # make sure we cover type 1 (fallback)
    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello.
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default.
    tcp-response content accept if serverhello

    # SSL session ID (SSLID) may be present on a client or server hello.
    # Its length is coded on 1 byte at offset 43 and its value starts
    # at offset 44.
    # Match and learn on request if client hello.
    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello.
    stick store-response payload_lv(43,1) if serverhello

    #option ssl-hello-chk

    server x_testdomain_stag 123.123.123.123:443


backend ssl_testdomain_prod
   mode tcp
   #option nolinger
   option tcplog
   balance roundrobin
   hash-type consistent
   option srvtcpka

    # maximum SSL session ID length is 32 bytes.
    stick-table type binary len 32 size 30k expire 30m

    # make sure we cover type 1 (fallback)
    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello.
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default.
    tcp-response content accept if serverhello

    # SSL session ID (SSLID) may be present on a client or server hello.
    # Its length is coded on 1 byte at offset 43 and its value starts
    # at offset 44.
    # Match and learn on request if client hello.
    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello.
    stick store-response payload_lv(43,1) if serverhello

    #option ssl-hello-chk

    server x_testdomain_prod 123.123.111.111:443

This example implies that you terminate your SSL on the webserver backends, I haven't tried to do this with haproxy ssl termination yet.

If that is what you want, maybe this example helps getting it to work.

there is another example that uses use_server instead of use_backend here

1
  • 1
    Thanks, this finally worked for me, after hours of searching online Apr 23, 2019 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.