Protecting services with client certificates using Haproxy

What we want to achieve #

We want to be able to connect to services inside a private network using client certificates, in this example we will be connecting to Redis.


Install or compile Haproxy #

I am using Debian, so this is what I use to compile Haproxy for testing out this setup.

apt-get -y install make gcc g++ libssl-dev

tar xzf haproxy-1.8.4.tar.gz
cd haproxy-1.8.4
make TARGET=generic USE_OPENSSL=1
make install PREFIX=/usr/local

Install and configure firewall #

I am testing with a Redis-server, but it can be anything. First firewall off everything except for port 22 (ssh) and port 88 (our external redis port):

apt-get -y install ufw

ufw default deny incoming
ufw default allow outgoing
ufw allow 22
ufw allow 88

Install Redis #

apt-get -y install redis-server

Since Debian and Debian-related distros have the terrible idea that software need to run by default, you should now have a running redis-server on port 6379.

Generate certificates #

This is just for testing, please use some better tooling like easy-rsa to manage your certificates:

openssl genrsa -nodes -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt # with
cat ca.crt ca.key > ca.pem

openssl genrsa -out client.key 4096
openssl req -new -key client.key -out client.csr #
openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
cat client.crt client.key > client.pem

Setup Haproxy server #

This let Haproxy listen on port 88, and it will verify the connecting certificates, and if the authentication is succesful, it will forward the traffic to redis on

Create a file named: haproxy_server.cfg:

  pidfile /var/run/
  maxconn 4000
  tune.ssl.default-dh-param 2048

listen redis
  bind ssl crt /root/ca.pem ca-file /root/ca.crt verify required
  mode tcp
  server redis-1 maxconn 1024

Run the config in a non-daemonized way:
haproxy -f haproxy_server.cfg.

Connecting to Redis through another Haproxy #

Create a file named: haproxy_client.cfg:

  maxconn 4000

listen redis
    mode tcp
  server redis-serv01 ssl crt client.pem ca-file ca.crt

Run the config in a non-daemonized way:
haproxy -f haproxy_client.cfg.

Now you should be able to connect to Redis from your terminal:

$ redis-cli -h localhost -p 7945
localhost:7945> ping

Connect to Redis through Ruby #

If you do not want to install a Haproxy instance just to manage the client certificates, you can connect directly from your favorite programming language, here is an example on how to do it in Ruby:

require 'socket'
require 'openssl'

sock ='', 88)

ctx =
ctx.cert ='/root/client.pem'))
ctx.key  ='/root/client.key'))
# ctx.key ='client.key'), 'secret_password')

@socket =, ctx).tap do |socket|
  socket.sync_close = true
  socket.write "PING\n"
  socket.each_line do |line|
    puts line

Todo: #

Consider using CRL-files for making your life easier, CRL stands for Client Revocation List, and it is a list of certificates that you no longer trust for one reason or another, so if a server gets hacked, or an employee laptop goes missing, you can revoke the certificate and sleep nicely afterwards knowing that they can’t use the certificate for anything.

When you have a crl-file, It is as easy as adding crl-file <crlfile> to the server configuration.

There is a nice article about crls here:


Now read this

Optimizing bicycling routes with OpenStreetMap and APIs

(There’s a video version of this article available here, where we also do a deep dive into the code behind it, remember to turn on subtitles): I’ve started playing with data from OpenStreetMap (OSM). It started with me trying to fetch... Continue →