As I’ve started to containerize, certain webapps of mine utilize SSL for secure communication. Hence, I usually combine everything the resulting webapp needs to serve the app using SSL, including certificates and keys.

HAProxy provides the ability to pass-through SSL via using tcp proxy mode. This is awesome, except you can forget about serving multiple domains/vhosts in this basic configuration. However, SNI to the rescue!

From the HAProxy blog, there is indeed a way for HAProxy to inspect the SSL negotiation and find the hostname, sent via the client through SNI:

SNI addresses this issue by having the client send the name of the virtual domain as part of the TLS negotiation

Let’s take a look at how I setup this site in haproxy (dockerfile/haproxy) configurations across a few of my different servers:

frontend https-in *:443
        mode tcp
        bind *:8443
       ## From http://byte-consult.be/2014/01/28/using-haproxy-as-an-ssl-gateway/
        ## Better described here: http://blog.haproxy.com/2012/04/13/enhanced-ssl-load-balancing-with-server-name-indication-sni-tls-extension/

		option socket-stats
        tcp-request inspect-delay 5s
        tcp-request content accept if { req_ssl_hello_type 1 }

        use_backend stn if { req_ssl_sni -i scriptthe.net }

And now for the backend declaration, where I call an nginx container that is listening on 2368 (-p 127.0.0.1:2368:443):

backend stn
  #balance leastconn
  mode tcp

  # maximum SSL session ID length is 32 bytes.
  stick-table type binary len 32 size 30k expire 30m

  acl clienthello req_ssl_hello_type 1
  acl serverhello rep_ssl_hello_type 2

  # use tcp content accepts to detects ssl client and server hello.
  tcp-request inspect-delay 5s
  tcp-request content accept if clienthello

  # no timeout on response inspect delay by default.
  tcp-response content accept if serverhello

  stick on payload_lv(43,1) if clienthello

  # Learn on response if server hello.
  stick store-response payload_lv(43,1) if serverhello

  option ssl-hello-chk

  server stn_container 172.17.42.1:2368 ## note that when doing mode tcp, you do not need the ending `ssl`

If you aren’t aware, 172.17.42.1 is the gateway address for our containers help by interface docker0. This is a way to access services running on our host from inside our container. See below for configuring iptables to allow this.

Finally, lets setup our *:80 listener to redirect permanent our users to our SSL enabled site:

frontend http-in
        bind *:80
        
		## Setup the other site we run on this server
		acl othersite hdr(host) -i othersite.net
        use_backend othersite_nginx if othersite

        ## And offload stn to ssl-enabled port
        redirect scheme https code 301 if { hdr_end(host) -i scriptthe.net } !{ ssl_fc }

One thing to note, since I utilize fairly strict iptables rules on my hosts, in this scenario, because I’m not linking the haproxy container with my stn-nginx container, we need to put a rule in the INPUT chain under the filter table (yes, that’s the correct naming for those) to allow traffic from -i docker0:

iptables -A INPUT -i docker0 -j ACCEPT

Please make sure you understand what this means: any process within a docker container can access any service running on the host machine!

That’s all folks!

PS: This is the manual way of doing things. The future is bright with haproxy-confd and nginx dynamic containers which provide the proxy layer in an on-the-fly modifiable way.

Mario Loria is a builder of diverse infrastructure with modern workloads on both bare-metal and cloud platforms. He's traversed roles in system administration, network engineering, and DevOps. You can learn more about him here.