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)

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.