Using HAProxy to offload SSL
At Qandidate.com we started to use Docker containers to run our apps and REST APIs. Some of them are publicly exposed and should communicate over a secure connection. Of course we can config nginx in our containers to accept secure connections, but I want to show you how easy it is to use HAProxy to do the SSL offloading.
Let's get started. In this post I used a sandbox Vagrant environment with Ubuntu 14.04. I set it up with the following Vagrantfile:
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/trusty64"
end
HAProxy has built-in SSL support starting with version 1.5. Ubuntu 14.04LTS ships with 1.4, so we have to add a repository with HAProxy 1.5:
$ sudo add-apt-repository ppa:vbernat/haproxy-1.5
$ sudo apt-get update
Install HAProxy 1.5 with the following command:
$ sudo apt-get install haproxy
Configure HAProxy to allow HTTPS connections by appending the following to your /etc/haproxy/haproxy.cfg
file.
frontend http-in
mode http
bind *:443 ssl crt .
Place the SSL certificate for your domain in /etc/ssl/private
or create a new self-signed one using the following command:
$ openssl req -x509 -new -nodes -newkey rsa:2048 -keyout site1.key -out site1.crt -days 365
Answer the questions accordingly. The only important question right now is "Common Name". Answer that question with the domain name (e.g. qandidate.com) you're generating the certificate for. In this example I created a certificate with common name site1
.
Now create a .pem
file by concatenating the .crt
and the .key
files.
$ cat site1.crt site1.key | sudo tee /etc/ssl/private/site1.pem
Tell HAProxy which back-end it has to use to route the traffic to. Use ssl_fc_sni
to test with certificate is used. The argument of ssl_fc_sni should match the "Common Name" of the certificate. In this example we used site1
.
Now update your /etc/haproxy/haproxy.cfg
:
frontend http-in
bind *:443 ssl crt .
use_backend backend_site1 if { ssl_fc_sni site1 }
Add the backend to the configuration file:
backend backend_site1
balance roundrobin
option httpclose
option forwardfor
server s1 127.0.0.1:8081 maxconn 32
Restart HAProxy.
$ sudo service haproxy restart
Multiple sites
To proxy to multiple sites, just create a certificate for the second site and add it to /etc/ssl/private/
as well.
Create a second certificate for site2. Enter site2
when asked for a Common name.
$ openssl req -x509 -new -nodes -newkey rsa:2048 -keyout site2.key -out site2.crt -days 365
$ cat site2.crt site2.key | sudo tee /etc/ssl/private/site2.pem
Tell HAProxy which backend to use.
frontend http-in
bind *:443 ssl crt .
use_backend backend_site1 if { ssl_fc_sni site1 }
use_backend backend_site2 if { ssl_fc_sni site2 }
Add the extra backend.
backend backend_site2
balance roundrobin
option httpclose
option forwardfor
server s2 127.0.0.1:8082 maxconn 32
Restart HAProxy.
Note: Repeat the steps to add more sites.
See it in action
To see HAProxy in action create two simple php websites.
$ sudo apt-get install php5-cli
$ mkdir site1
$ mkdir site2
$ echo -e "<?php\n\necho 'site1\\n';" > site1/index.php
$ echo -e "<?php\n\necho 'site2\\n';" > site2/index.php
Start the web servers with the following commands
$ php -S 127.0.0.1:8081 -t site1 &
$ php -S 127.0.0.1:8082 -t site2 &
Finally add the following lines to your /etc/hosts
127.0.0.1 site1
127.0.0.1 site2
See it in action:
$ curl -o - -k https://site1
$ curl -o - -k https://site2
-k
is used to allow self signed certificates.
See how easy it is to use HAProxy to do the SSL offloading. The hard part was generating the certificates ;). In a following post I want to show how to only accept connections from trusted clients by being your own certificate authority and signing your own certificates.