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>