HAProxy SNI
Last time, I looked at configuring Server Name Indication (SNI) with Apache. It just so happened I needed to set up SNI with the HAProxy Load Balancer last week, so let’s take a look at that. Typically, when you are hosting multiple sites with HAProxy, you do something like:
use_backend app1-servers if { hdr(host) -i app1.example.com }
use_backend app2-servers if { hdr(host) -i app2.example.com }
use_backend app3-servers if { hdr(host) -i app3.example.com }
This selects the backend to use based on the HTTP Host header.
When you add HTTPS to the mix, there are two ways that HAProxy can handle it, either by terminating SSL or by passing it through.
When HAProxy is terminating SSL, it has the SSL cert and is responsible for encrypting and decrypting the traffic. It may also talk to the backend using HTTPS, but on secure internal network this is usually skipped. In ASCII it looks like:
client <--> HTTPS <--> HAProxy <--> HTTP <--> backend server
When HAProxy is passing though HTTPS traffic it simple sends the raw TCP stream through to the backend which has the certificate and handles encryption and decryption.
HAProxy HTTPS setups can be a little tricky. So make sure you have a working one first before adding SNI to the mix.
When using HAProxy to terminate HTTPS connections, you bind a front end to port 443, and give it an SSL certificate:
frontend https
bind *:443 ssl crt /etc/haproxy/ssl/app1.example.com.pem
The .pem
file needs to contain the private key, the certificate, and
any intermediate certificate as well. Something like:
cat example.com.key example.com.crt bundle.crt > example.com.pem
should do the trick.
All you need to do to enable SNI is to be give HAProxy multiple SSL certificates:
frontend https
bind *:443 ssl crt /etc/haproxy/ssl/app1.example.com.pem /etc/haproxy/ssl/app2.example.com.pem /etc/haproxy/ssl/app3.example.com.pem
That will cause the right certificate to be automatically selected. After that the Host header can be used just as it would be for HTTP.
In pass-through mode SSL, HAProxy doesn’t have a certificate because it’s not going to decrypt the traffic and that means it’s never going to see the Host header. Instead it needs to be told to wait for the SSL hello so it can sniff the SNI request and switch on that:
frontend https
bind *:443 ssl
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend app1-servers if { req_ssl_sni -i app1.example.com }
use_backend app2-servers if { req_ssl_sni -i app2.example.com }
use_backend app3-servers if { req_ssl_sni -i app3.example.com }
Once again, SNI is simple and easy, so why aren’t you using it?
Comments