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
Logging
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 $UDPServerAddress 127.0.0.1
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.
You can also create a self-signed SAN certificate.
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
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
#------------------
global
log /dev/log local0 debug
log /dev/log local1 notice
user haproxy
group haproxy
chroot /var/lib/haproxy
daemon
debug
stats socket /var/lib/haproxy/stats mode 660 level admin
maxconn 2000
tune.ssl.default-dh-param 2048
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
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
#------------------
defaults
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
bind 0.0.0.0:8998
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
# use to send simple file response
errorfile 503 /etc/haproxy/200.http
# use real backend later
#default_backend www-backend
#------------------
# backend instances
#------------------
backend www-backend
balance roundrobin
cookie WWWID insert indirect nocache
redirect scheme https if !{ ssl_fc }
server mypool1 127.0.0.1:8080 cookie www1 check inter 3000
#server mypool2 127.0.0.1:8080 cookie www2 check inter 3000
#server mypool3 127.0.0.1:8080 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.
So that you don’t have to setup a pool of backing servers right now, create “/etc/haproxy/200.http” to serve a simple static file when accessed.
HTTP/1.0 200 Found Cache-Control: no-cache Connection: close Content-Type: text/html <html> <head> <title>Up</title> </head> <body> <p>Up</p> </body> </html>
Later on, when your real backing application servers are running, you should remove the ‘errorfile’ line, and uncomment “default_backend www-backend” so real content is delivered.
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/SAN in the certificate.
Start your research here and here on how to harden HAproxy security.
REFERENCES
https://launchpad.net/~vbernat/+archive/ubuntu/haproxy-1.7
http://serverfault.com/questions/645924/haproxy-logging-to-syslog
https://serversforhackers.com/load-balancing-with-haproxy
https://www.haproxy.com/doc/aloha/7.0/haproxy/healthchecks.html
NOTES
Create “/etc/haproxy/200.http” to serve simple static file from front end
HTTP/1.0 200 Found Cache-Control: no-cache Connection: close Content-Type: text/html <html> <head> <title>Up</title> </head> <body> <p>Up</p> </body> </html>