Load Balance multiple frontends and backends with Haproxy

Alex Moran
5 min readDec 17, 2019

--

Photo by Kevin Ku on Unsplash

The goal here is to have Haproxy as our frontline. Then behind that we will have multiple backend servers as well as multiple frontends.

While I was looking around to figure out Haproxy and how I could have 1 public IP and link multiple sites to that as well as improving security by having the vm Haproxy is running on as another hurdle for any potential attackers to work through.

Global Settings

Starting with the global settings sets some standard options like the user and group. Maxconn relates to the maximum allowed connections.

The ssl parameters are from this config site . Makes it a lot easier to specify what you want to allow.

https://ssl-config.mozilla.org/#server=haproxy&server-version=1.9.8&config=intermediate

global
log 127.0.0.1 local0 notice
maxconn 6000
user haproxy
group haproxy
tune.maxrewrite 12192
tune.bufsize 32000
tune.ssl.default-dh-param 4048
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-P$
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20$
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets

Listen Stats(Haproxy built in web view of all servers/sites)

Bind is you select the port with where you want to view the stats and in the auth section set your chosen username and password.

listen statsbind :1234 mode httpstats enablestats hide-versionstats realm Haproxy\ Statisticsstats uri /stats auth <username>:<password>

Listening and http-in

The first part the listen is a tcp port forward option to passthrough Haproxy and connect straight to the server behind in this case I will connect to my Haproxy IP or URL and add the port 1234 to connect straight through to the mysite.myserver.com backend.

The second part is the standard frontend http_in this is where you bind port 80 as the default non ssl traffic to travel to as well as the redirect if required.

The final part is the https section. This setup uses SSL termination and passthrough so it requires a copy of your cert and key file combined into a single file be stored within the haproxy server. If you have multiple individual certs I personally have found that they need to be in a single line not on multiple lines, its possible that their is a better method I just have not had the time to find one yet.
The end of this part is adding in support for alpn h2 and http 1.1

listen mysite_connect
bind :1234
mode tcp
default_backend mysite.myserver.com
frontend http_in
bind *:80
redirect scheme https code 301 if !{ ssl_fc }
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/mysite.myserver.com.pem crt /etc/haproxy/certs/mysite.myserver.com.pem alpn h2,http/1.1

Routing the incoming requests

Using acl and host we can specify where the incoming url requests go to and what backend they go to as well. The first part contains the incoming url and setting the host info and we tell Haproxy which backend to use depending on the host.

acl host_myserver.com hdr(host) -i mysite.myserver.com
acl host_myserver.com hdr(host) -i analytics.myserver.com
use_backend myserver if host_myserver.com

Backend routing and management

We have two types of backends specified here due to calling the

listen mysite_connect

This has a default backend set which is loaded like the check sends checks to the server to check if its up or down and responding to different layer queries.

Which you can view in the stats page we set up earlier.

backend mysite.myserver.com
mode tcp
server mysite.myserver.com <server_ip>:22 check

The http backend setup is similar to the above but can be expanded.

The backend is the name that you call above in the use_backend section.

The balance — Haproxy have a number of options, Round Robin will just add one connection at a time to each server.

Mode http — sets the request type to forward.

Option — Many options to include if you require something specific, httpclose will close a connection once it is completed. The default is to keep alive connections.

Cookie — the cookie is created using the server id which is the first part. after server. The idea is that if you use multiple frontends for the same web app that you can have sticky sessions by using the cooking and then checking the cookie against whats set.

Server — specify the server or servers if you have multiple. To include server ip and port depending if you want ssl or no-ssl, the check for haproxy to test if server is still alive as does the send-proxy is an additional layer of verify.

backend myserverbalance roundrobinmode httpoption httpclosecookie SERVERID insert indirect nocacheserver mysite1.myserver.com <server_ip>:443 check cookie mysite1.myserver.com check ssl verify none send-proxy-v2server mysite2.myserver.com <server_ip>:443 check cookie mysite2.myserver.com check ssl verify none send-proxy-v2

Conclusion

Once you combine all the above you have a haproxy config that can support an ever expanding list of sites and servers with minimal effort and a single public ip to manage many sites.

This config is used in production to manage multiple moodle sites and company websites and a few development sites for testing purposes.

This does mean you have a single point of failure if you only use one vm but it requires such minimal resources you can run multiples but that is not really within the scope for this piece.

If you have any questions don’t hesitate to ask and I hope this helps someone.

Final tips

If you are having issues generating ssl certs using lets-encrypt and certbot in one of your backends try using acme-sh it works a lot better and supports alpn which works through haproxy. If not create a non-ssl backend and change your host_site to the non-ssl backend create the cert and then change it back and you will have it working.

Thank you for reading. If you enjoy or find this helpful you could look at joining Medium. Its €5 a month you can sign up using this link or if you sign up using my link I will get a small commission from it which is https://medium.com/@alexrmoran/membership

--

--

Alex Moran
Alex Moran

Written by Alex Moran

Full-Stack Developer looking for the next big thing. If you want to support me please sign up using my link below https://medium.com/@alexrmoran/membership

No responses yet