Haproxy multiple certificates over single IP using SNI
Hello!, I'm a fullstack/devops developer who is going to start sharing solutions to problems around. Over the last two years i have specialized on Kubernetes/Docker, NodeJS, Java and Angular/React. I will be writing about software solutions in the next posts.
I had a problem with routing in haproxy and i didn't know why, i had the haproxy doing routing with SNI(Server name indication), which is an extension of the TLS protocol which is useful in order to get the hostname at the start of the handshake, so i could do load balancing over multiple backends without doing handshake. But i found that if you do routing with SNI and present the same certificate in multiple hostname you won't always get the same result.
To see the things clearly let's present an example.
A configuration of haproxy with SNI routing could be like this:
global
log /dev/log local0
log /dev/log local1 notice
stats socket /haproxy-admin.sock mode 660 level admin
stats timeout 30s
daemon
defaults
maxconn 1000
mode http
log global
option dontlognull # bind *:443 ssl crt .
timeout http-request 5s
timeout connect 5000
timeout client 2000000 # ddos protection
timeout server 2000000 # stick-table type ip size 100k expire 30s store conn_cur
frontend env_ssl_frontend
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 10s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend bk_app1 if { req.ssl_sni -m end app1.domain.com }
use_backend bk_app2 if { req.ssl_sni -m end app2.domain.com }
use_backend bk_app3 if { req.ssl_sni -m end app3.domain.com }
backend bk_app1
balance source
mode tcp
option ssl-hello-chk
server main 127.0.0.1:3000
backend bk_app2
balance source
mode tcp
option ssl-hello-chk
server main 127.0.0.1:3001
backend bk_app3
balance source
mode tcp
option ssl-hello-chk
server main 127.0.0.1:3002
The frontend looks pretty clear, we have it listening on 443 on TCP mode (L4) and routing to bk_app1, app2 or app3, depending on the server_name coming from the Client Hello, and then, sends the traffic to the corresponding App.
In the backend server you will have to terminate the handshake, if not, it will not work.
You can test this easily without having to install haproxy by having Docker installed having a file named haproxy.cfg with the previous config:
docker run -p 443:443 --name haproxy -v "${PWD}":/usr/local/etc/haproxy:ro haproxy:1.8
If you want to see which server_name is being sended, you can use Wireshark
With this configuration, if you present the same certificate in bk_app1 and bk_app2, you will be redirected based on the current handshake. So, you access app1.domain.com, then access app2.domain.com, then app2.domain.com will routed to bk_app1.
So always that you do routing over SNI, you have to do redirecto to the same backend per certificate!
I hope it helped!
I'm planning on writing about microservices, CI/CD and Kubernetes. So if you want me to talk about any themes feel free to ask!
Top comments (0)