How to get HAProxy to route TCP based on SNI (using openssl s_client to test)?

8,015

The answer is to use ssl_fc_sni, instead of req.ssl_sni. The former is for SSL-terminated sessions, whereas the latter is for sessions where TCP is passed straight through.

Share:
8,015

Related videos on Youtube

John
Author by

John

Updated on September 18, 2022

Comments

  • John
    John almost 2 years

    I want to use HAProxy to terminate TLS-encrypted TCP connnections and to pass the unencrypted TCP traffic to various backends based on the Server Name Indication used to initiate the TLS connection.

    I have 3 services running on a backend server, each on a different port (5001, 5002, 5003). HAProxy binds to port 5000. I'd like to route connections to the first 2 servies by name or to the third if there is not a match. I am initiating the connecton to HAProxy using openssl s_client. However, in the logs I can see that the connections are only ever routed to the default server, i.e. the SNI seems to be ignored.

    DNS:

    A record     demo.sni.example.com     1.2.3.4
    CNAME        1.sni.example.com        pointing to demo.sni.example.com   
    CNAME        2.sni.example.com        pointing to demo.sni.example.com   
    

    i.e. I'd like the following routing to occur:

    SNI = 1.sni.example.com:5000 -> 1.2.3.4:5001
    SNI = 2.sni.example.com:5000 -> 1.2.3.4:5002
    anything else on port 5000 -> 1.2.3.4:5003
    

    haproxy.cfg:

    global
      log stdout  format raw  local0 info
    
    defaults
      timeout client 30s
      timeout server 30s
      timeout connect 5s
      option tcplog
    
    frontend tcp-proxy
      bind :5000 ssl crt combined-cert-key.pem
      mode tcp
      log global
      tcp-request inspect-delay 5s
      tcp-request content accept if { req_ssl_hello_type 1 }
    
      use_backend bk_sni_1 if { req.ssl_sni -i 1.sni.example.com }
      use_backend bk_sni_2 if { req.ssl_sni -i 2.sni.example.com }
      default_backend bk_default
    
    backend bk_sni_1
      mode tcp
      log global
      balance roundrobin
      server server1 1.2.3.4:5001 check
    
    backend bk_sni_2
      mode tcp
      log global
      balance roundrobin
      server server1 1.2.3.4:5002 check
    
    backend bk_default
      mode tcp
      log global
      balance roundrobin
      server server1 1.2.3.4:5003 check
    

    combined-cert-key.pem is a self-signed certificate file plus key where the CN is the IP of the server (1.2.3.4) and there are SANs of all DNS values and the IP.

    Connections initiated using openssl s_client:

    I have tried connecting via DNS (A & CNAME records, as well as IP):

    echo test | openssl s_client -connect demo.sni.example.com:5000 -servername 1.sni.example.com
    echo test | openssl s_client -connect 1.sni.example.com:5000 -servername 1.sni.example.com
    echo test | openssl s_client -connect 1.2.3.4:5000 -servername 1.sni.example.com
    

    However, all connections are routed to the default backend bk_default.

    What is causing HAProxy to not recognise the SNI servername? (I am using the latest HAProxy docker image: https://hub.docker.com/_/haproxy)