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.