HAProxy: Using HAProxy for SSL termination on Ubuntu

HAProxy is a high performance TCP/HTTP (Level 4 and Level 7) load balancer and reverse proxy.  A common pattern is allowing HAProxy to be the fronting SSL-termination point, and then HAProxy determines which pooled backend server serves the request.

Installation from Repository

Although HAProxy is in the main Ubuntu repository, the version is 1.4 and we really need access to the newer feature set.   The configuration files in this article are aimed at HAProxy >= 1.6.

You have a couple of options for apt-get installation.  The Debian HAProxy packaging team offers these packages, but I prefer Vincent Bernat’s PPA.   Feel free to go whichever way you feel comfortable, below are the steps I use for 1.7:

# apt-cache policy haproxy
# apt-get install python-software-properties software-properties-common -y
# add-apt-repository ppa:vbernat/haproxy-1.7
# apt-get update
# apt-cache policy haproxy
# apt-get install haproxy -y
# haproxy -v


HAProxy sends log messages via rsyslog, so we must make sure this is configured properly.  The installation created a config file ‘/etc/rsyslog.d/49-haproxy.conf’ which will route the files to ‘/var/log/haproxy.log’.

Modify ‘/etc/rsyslog.conf’ so that it is listening on UDP port 514:

$ModLoad imudp 
$UDPServerRun 514 

And then restart the rsyslog service:

# service rsyslog restart

Secure Certificate

If you are going to use HAProxy as an SSL termination point, then it needs a private/public key pair.  The easiest way to satisfy this requirement is to create a self-signed certificate as described in the article I wrote here.

Open Firewall Ports

We need to make sure port 80 and 443 are open.  And then the HAProxy monitoring port on 8998.

# ufw allow 80/tcp
# ufw allow 443/tcp
# ufw allow 8998/tcp

SSL Termination for pool

The file ‘/etc/haproxy/haproxy.conf’ is the configuration file that defines all options and frontend/backends.  Place the contents below into the file:

# Global settings
    log /dev/log    local0 debug
    log /dev/log    local1 notice
    user haproxy
    group haproxy
    chroot  /var/lib/haproxy
    stats socket /var/lib/haproxy/stats mode 660 level admin
    maxconn 2000
    tune.ssl.default-dh-param   2048
    ssl-default-bind-options    no-sslv3 no-tlsv10
    # no-tlsv11

# common defaults that all the 'listen' and 'backend' sections will
# use- if not designated in their block
    log global
    mode http
    retries 3
    option      http-keep-alive
    option      httplog
    option      dontlognull
    option      forwardfor
    option      http-server-close
    log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ %{+Q}r
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

# internal statistics
listen stats
    mode http
    stats auth Admin:password
    stats enable
    stats realm Haproxy\ Statistics
    stats refresh 20s
    stats uri /admin?stats

# frontend instances
frontend unsecured
    bind        *:80
    reqadd      X-Forwarded-Proto:\ https
    redirect    scheme https code 301 if !{ ssl_fc }
    default_backend www-backend

frontend www-https
    bind        *:443 ssl crt /etc/pki/tls/certs/FQDN.pem
    log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tq/%Tw/%Tc/%Tr/%Tt\ %ST\ %B\ %CC\ %CS\ %tsc\ %ac/%fc/%bc/%sc/%rc\ %sq/%bq\ %hr\ %hs\ %{+Q}r\ ssl_version:%sslv\ ssl_cipher:%sslc
    # passing on that browser is using https
    reqadd X-Forwarded-Proto:\ https
    # for Clickjacking
    rspadd X-Frame-Options:\ SAMEORIGIN
    # prevent browser from using non-secure
    rspadd Strict-Transport-Security:\ max-age=15768000
    default_backend www-backend

# backend instances
backend www-backend
    balance roundrobin
    cookie WWWID insert indirect nocache
    redirect scheme https if !{ ssl_fc }
    server mypool1 cookie www1 check inter 3000
    #server mypool2 cookie www2 check inter 3000
    #server mypool3 cookie www3 check inter 3000

You need to replace ‘FQDN’ with the actual fully qualified domain name of your Nginx host for the certificate .pem name.  And of course, point the upstream servers to your actual pool of host:port.

All non-secure requests are redirected to HTTPS for security.

The monitoring statistics can be reached at http://<haproxy>:8998/admin?stats

Start Service

To start HAProxy, use the command below.  Logs can be found at ‘/var/log/haproxy.log’.

# service haproxy start

Be sure that when you pull up the HAProxy server in the browser, you use the fully qualified host name (and not the IP).  If you have to make changes to your local hosts file, then do so, because the browser address needs to match the CN in the certificate.

Start your research here and here on how to harden HAproxy security.









load balancing, affinity, persistence, sticky sessions: what you need to know