Building Haproxy from source

Written by on Modified on Read time: 4 min

I had had enough of running Nginx as mostly a reverse proxy for all of my websites. HAProxy seemed like the next step in powering my public facing websites, with Lighttpd as a backend for static websites.

Why and how?

To sum it up: Because I have so many different backends. I have the standard PHP7 requirements, some static websites and lots of applications that have their own webserver built-in.

Nginx can do all of that fine, but it lacks some advanced TLS features that I want. Mainly, I want to deny non-SNI requests, because I don’t need that much backwards compatibility, and I don’t want to have anything responding without SNI.

I decided to build HAProxy from source to get the newest features, like OpenSSL 1.1.1, which supports TLS1.3.

I also wanted to port my config from Nginx to HAProxy. This blog post will not be detailing this progress though.

Building

Now if you’re never built anything from source, this can seem like a frightening thing to do. And if you have, you know that there’s nothing to fear. Whichever camp you’re in, at least the commands below may prove helpfull if you want to quickly build the newest stable version of HAProxy.

1
sudo apt install build-essential libpcre2-dev zlib1g-dev git wget

Now I’m giving you the commands that use links that are currently up to date, but if you’re reading this later on you’ll need to replace the version numbers with newer ones.

OpenSSL

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
su
cd /usr/local/src/
git clone git://git.openssl.org/openssl.git
cd openssl
git branch -a
# Use the latest stable branch from all branches
git checkout OpenSSL_1_1_1-stable
# Rpath is needed for haproxy.
./config -Wl,-rpath=/opt/openssl-1.1.1/lib --prefix=/opt/openssl-1.1.1 --openssldir=/opt/openssl-1.1.1 shared no-ssl3
make
make test
make install

HAProxy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
su
cd /usr/local/src/
git clone https://git.haproxy.org/git/haproxy.git
cd haproxy
git tag
# change v2.0.0 to the latest stable version from git tag.
git checkout v2.0.0
# Read the INSTALL file for what the `make` command parameters do and tailor them to your own needs. Below is what I used.
# Use CPU=native if you're not in a virtualized enviroment. Systemd requires `sudo apt-get install libsystemd-dev`
make TARGET=linux_glibc CPU=generic USE_PCRE2_JIT=1 USE_PCRE2_JIT=1 USE_OPENSSL=1 SSL_LIB=/opt/openssl-1.1.1/lib SSL_INC=/opt/openssl-1.1.1/include USE_ZLIB=1 USE_LIBCRYPT=1 USE_LINUX_TPROXY=1 USE_LINUX_SPLICE=1 USE_SYSTEMD=1 USE_CRYPT_H=1
make install

If ou cannot call haproxy --help yet, then use the following enviroment variable LD_LIBRARY_PATH=/opt/openssl-1.1.1/lib/ haproxy --help .

Configuration

Systemd service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# /etc/systemd/system/haproxy.service
[Unit]
Description=HAProxy
After=network.target

[Service]
# LD_LIBRARY_PATH might not be needed if it works straight away for you.
Environment="LD_LIBRARY_PATH=/opt/openssl-1.1.1/lib/" "CONFIG=/etc/haproxy" "PIDFILE=/var/run/haproxy.pid"
ExecStartPre=/usr/local/sbin/haproxy -f $CONFIG -c -q
ExecStart=/usr/local/sbin/haproxy -Ws -f $CONFIG -p $PIDFILE
ExecReload=/usr/local/sbin/haproxy -f $CONFIG -c -q
ExecReload=/bin/kill -USR2 $MAINPID
KillMode=mixed
Restart=always
Type=notify

[Install]
WantedBy=multi-user.target
1
2
3
4
mkdir /etc/haproxy
mkdir /etc/haproxy/conf
mkdir /etc/haproxy/certs
vim /etc/haproxy/conf/default.cfg

Then to just write the HAProxy config… Easier said that done for me, since I knew pretty much nothing about how HAProxy does things.

I spent hours trying to get it to work the way I wanted: Ignore requests without SNI, and serve multiple domains and multiple subdomains flexibly. Below is what I finally have as of writing this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Now in /etc/haproxy/conf/anyfilename
global
  maxconn 4096
  tune.ssl.default-dh-param 2048
  ssl-dh-param-file /etc/haproxy/dhparams.pem
  ssl-default-bind-options no-sslv3 no-tls-tickets
  ssl-default-bind-ciphers DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA
  #debug

defaults
  mode http
  timeout http-request 5s
  timeout connect 5s
  timeout client 15s
  timeout server 30s
  option http-server-close

frontend http-redirect
  bind *:80
  redirect scheme https code 301

frontend https-in
  bind *:443 ssl strict-sni crt /etc/haproxy/certs/
  option forwardfor

  # It matches if the HTTP Host: field mentions any of the hostnames (after the '-i')
  acl use_lighttpd hdr(host) -i blog.ljoonal.xyz tos.ljoonal.xyz

  use_backend lighttpd if use_lighttpd

  errorfile 503 /var/www/html/index.html

backend lighttpd
  option forwardfor
  server lighttpd localhost:8080

It’s probably not good at all, but hey, it works! I excluded setting up lighttpd, or any other service to work as the backend, because that’s not really relevant to lighttpd.

PS, here’s a protip: run haproxy -c -V -f /etc/haproxy/conf/ to see if your configuration works before restarting or reloading it!