Install Haproxy 1.5 as transparent loadbalancer for http and https on Ubuntu Trusty


I've been searching and testing this quite a while until I had a working setup. There are a lot of articles out there, but here is a solution that has worked for me.

First install haproxy 1.5:

apt-add-repository ppa:vbernat/haproxy-1.5
apt-get update
apt-get install haproxy

It is important that you use at least version 1.5 of haproxy!

First of all edit /etc/default/haproxy and change the line ENABLED=1. You also may want to change some flags, for example to give haproxy more memory to work with (256MB in this case): EXTRAOPTS="-de -m 256"

The actual configuration file is /etc/haproxy/haproxy.cfg.

If you only want to loadbalance http traffic you can use a listen block like this:

listen appname 0.0.0.0:80
    mode http
    balance roundrobin
    option httpclose
    option forwardfor
    server lamp1 10.0.0.1:80 check
    server lamp2 10.0.0.2:80 check

You should change appname to a name that suits your application and 0.0.0.0 to your public IP address if you do not want haproxy to listen on all IP addresses. Incoming requests on http port 80 are balanced to 10.0.0.1 and 10.0.0.2. Change these IPs accordingly. You may put only one or several servers.
It is important however to keep the options httpclose and forwardfor in order to be able to identify the remote client IP on your backend servers (X-Forwarded-For header) – else you'll only see the haproxy IP which is quite annoying and will prevent some things like geoip from working.

So far it's quite easy, isn't it? When coming to https, we cannot use the listen block, we have to use frontend and backend:

frontend appname-frontend
    mode http
    bind 0.0.0.0:80
    bind 0.0.0.0:443 ssl crt /etc/haproxy/certs/default.pem crt /etc/haproxy/certs
    option httpclose
    option forwardfor
    reqadd X-Forwarded-Proto:\ https if { ssl_fc }
    default_backend appname-backend

backend appname-backend
    mode http
    server lamp1 10.0.0.1:80 check

So here the frontend is listening on both, http and https ports and forwarding http traffic to the backend server 10.0.0.1. haproxy is doing the SSL Offload.

In case of an https connection, haproxy will look for your certificate in /etc/haproxy/certs/ using SNI (Server name identification). If it does not find a corresponding certificate it will serve the default.pem certificate.

So for example if you call https://www.example.net, your certificate file should be /etc/haproxy/certs/example.net.pem.
To have a .pem file you can simply put your private key and certificate in one file with .pem extension -no line break at the end of the file: 

cat example.net.key example.net.crt > example.net.pem

The reqadd line will add a special header in case of https which will provide you with an environment variable that helps you specify wether your on https or http. Remind that haproxy in this case does all the ssl handling and only sends http traffic to your servers!!

In the above example we're using one frontend for http and https. If you want to force your clients to use https, you can add this line to your frontend block, right before the default_backend line: redirect scheme https code 301 if !{ ssl_fc }

Of course you can also use different frontends for http and https, here's an example:

frontend appname-frontend-http
    mode http
    bind 0.0.0.0:80
    option httpclose
    option forwardfor
    default_backend appname-backend

frontend appname-frontend-https
    mode http
    bind 0.0.0.0:443 ssl crt /etc/haproxy/certs/default.pem crt /etc/haproxy/certs
    option httpclose
    option forwardfor
    reqadd X-Forwarded-Proto:\ https
    default_backend appname-backend

backend appname-backend
    mode http
    server lamp1 10.0.0.1:80 check

Why use different frontends? Here's an example: you might want to redirect some virual hosts to https and keep others on http – depending on the hostname. to achieve this, add the following two lines in your http frontend block, right before default_backend:

acl is_https_host hdr_end(host) -i example.com
redirect scheme https code 301 if is_https_host

Finally as a security measure you should put these 2 lines in your global block on top of the file:

ssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
ssl-default-bind-options no-sslv3

It will disable some unsecure ciphers/protocols and keep you safe from some attacks.

A note on using tcp mode to loadbalance https:
If using tcp, it will work of course, your webservers will receive https traffic and haproxy will not do the SSL offload. But: on your backend servers you will not be able to see the client IPs, you'll only see haproxy's IP which is quite annoying. However this is not a limitation of haproxy. HTTP is on OSI layer 7 and TCP on OSI layer 4. Options like http-close or forwardfor will be ignored when using TCP.

Nevertheless a quick example:

listen appname 0.0.0.0:443
    mode tcp
    balance roundrobin
    server lamp1 10.0.0.1:443 check
    server lamp2 10.0.0.2:443 check

When you've finished your configuration file, just restart haproxy: service haproxy restart

One final comment on GeoIP: when using apaches geoip module, you should add this line in /etc/mods-enabled/geoip.conf for it to work correctly behind haproxy: 

GeoIPScanProxyHeaders On