DEV Community

Dviejo
Dviejo

Posted on

Haproxy multiple certificates over single IP using SNI.

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

If you want to see which server_name is being sended, you can use Wireshark

Wireshark image

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)