Snapt load balancer not working

Oh SNAPT! Where's my load balancer?

HAProxy Updated on 7 mins

I was genuinely shocked and a little sad hearing the news that SNAPT has gone bust. They were a great company, with a nice product and more importantly — nice people. Our support team has been busy helping very stressed customers convert production sites to our commercial offering — but what if you don't have the budget for that?

Have you recently been left high and dry by your previous service provider, with just an HAProxy configuration file and nowhere to go? In this article, we will show you how easy it is to get an HAProxy-based replacement for SNAPT up and running in minutes, with just your config file to hand.

How to move from SNAPT to HAProxy for free:

Firstly, before we can get started, we'll need a Linux-based server which will act as our host for HAProxy. For this blog, we'll be using Ubuntu Server 22.04, a Debian-based Linux distribution, with HAProxy 2.0 available within its software repositories.

Installing HAProxy

To install the HAProxy service, first connect to the server over SSH and issue the following command:

sudo apt install haproxy

You should then see something similar to:

Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed
  haproxy
0 to upgrade, 1 to newly install, 0 reinstalled, 0 to remove and 93 not to upgrade.
Need to get 1,519 kB of archives.
After this operation, 3,288 KB of additional disk space will be used.
Get:1 http://gb.archive.ubuntu.com/ubuntu focal-updates/main amd64 haproxy amd64 2.0.13-2ubuntu0.5 [1,519 kB]
Fetched 1,519 kB in 0s (21.7 MB/s)
Selecting previously unselected package haproxy.
(Reading database ... 365637 files and directories currently installed.)
Preparing to unpack .../haproxy_2.0.13-2ubuntu0.5_amd64.deb ...
Unpacking haproxy (2.0.13-2ubuntu0.5) over (2.0.13-2ubuntu0.5) ...
Setting up haproxy (2.0.13-2ubuntu0.5) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for rsyslog (8.2001.0-1ubuntu1.3) ...
Processing triggers for systemd (245.4-4ubuntu3.17) ...

To ensure the service is enabled, issue the following command:

sudo systemctl enable haproxy.service

To check the service is running, use:

sudo systemctl status haproxy.service

...which will output something similar to:

● haproxy.service - HAProxy Load Balancer
     Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-08-31 13:02:59 BST; 1h 16min ago
       Docs: man:haproxy(1)
             file:/usr/share/doc/haproxy/configuration.txt.gz
   Main PID: 1699154 (haproxy)
      Tasks: 7 (limit: 18885)
     Memory: 2.9M
     CGroup: /system.slice/haproxy.service
             ├─1699154 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock -sf 1699155 -x /run/haproxy/admin.sock
             └─1780375 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock -sf 1699155 -x /run/haproxy/admin.sock

Your HAProxy configuration

In this example, we were able to obtain our HAProxy configuration from the Nova appliance using:

cat /etc/nova/haproxy/adcs/http451.cfg

...which will show your HAProxy configuration as follows (example shown):

frontend nova-http-451
bind 0.0.0.0:443
maxconn 100000
tcp-request content accept if { src -f /etc/nova/rulesets/whitelist.lst }
tcp-request content reject if { src -f /etc/nova/rulesets/blacklist.lst }
http-request set-header X-Nova-SRC %[src]
http-request set-header X-Nova-Country %[src,map_ip(/etc/nova/rulesets/ip2country.map)]
option httplog
log 127.0.0.1:12346 local0
capture request header Host len 64
capture request header User-Agent len 200
capture response header Server len 20
capture response header Content-Type len 64
mode http
option forwardfor
option http-server-close
timeout http-request 15s
http-response set-header Server NOVA
http-request track-sc0 src table per_ip_rates
http-request track-sc1 url32+src table per_ip_and_url_rates unless { path_end .css .js .png .jpeg .gif .woff .jpg }
http-request allow if { src -f /etc/nova/rulesets/whitelist.lst }
default_backend int-be-http-451-test
backend int-be-http-451-test
mode http
option accept-invalid-http-response
balance roundrobin
server test0 52.15.129.74:443  weight 10 check rise 5 fall 3 inter 2000

Importing your HAProxy configuration

Now that we have our HAProxy configuration, we can now import this into our new HAProxy service, that is running on our Ubuntu server.

The default HAProxy config file can be found in /etc/haproxy/haproxy.cfg. By using nano, we can edit the default haproxy config file and append the above to our existing configuration.

First, take a back-up the default config file:

sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.backup

Then edit the haproxy.cfg file and append the Nova config to the end of it:

sudo nano /etc/haproxy/haproxy.cfg

After we've finished editing the haproxy.cfg file, adding our Nova config, use CTRL+X to save the file with the changes made.

Validating the HAProxy configuration

Before we restart the HAProxy service, we can check that the updated configuration file passes the validation. To do this we can issue the following command:

haproxy -f /etc/haproxy/haproxy.cfg -c

But wait, I have validation errors!

After we run the validation tool, we may encounter some errors, for example:

# haproxy -f /etc/haproxy/haproxy.cfg -c

[ALERT] 242/115957 (1626912) : parsing [/etc/haproxy/haproxy.cfg:43]: 'http-request set-header': failed to parse sample expression <src,map_ip(/etc/nova/rulesets/ip2country.map)> : invalid args in converter 'map_ip' : failed to open pattern file </etc/nova/rulesets/ip2country.map>.
[ALERT] 242/115957 (1626912) : Error(s) found in configuration file : /etc/haproxy/haproxy.cfg

Let's fix this...

There are a few things we need to do to fix this, as there are a number of files missing after we've added the Nova config to our new HAProxy installation.

Let's create the missing folders:

sudo mkdir -p /etc/nova/rulesets

Now we create the files that are referenced within out HAProxy config file:

sudo touch /etc/nova/rulesets/ip2country.map
sudo touch /etc/nova/rulesets/whitelist.lst
sudo touch /etc/nova/rulesets/blacklist.lst

Missing backends

In addition to resolving the missing files, there are a couple of backend declarations that are missing from the config file, which are referenced within the Nova configuration. These are mainly to do with Bot Protection and will address rate-limiting and automated content scrapers to name a few. You can read more about this, here: Bot Protection with HAProxy.

Add the following to the end of the haproxy.cfg file:

backend per_ip_and_url_rates
    stick-table type binary len 8 size 1m expire 24h store http_req_rate(24h)

backend per_ip_rates
    stick-table type ip size 1m expire 24h store gpc0,gpc0_rate(30s)

The complete configuration file should now look like this:

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
        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-C>
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000
        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

#
# Nova HAProxy Config.
#
frontend nova-http-451
  bind 0.0.0.0:443
  maxconn 100000

  tcp-request content accept if { src -f /etc/nova/rulesets/whitelist.lst }
  tcp-request content reject if { src -f /etc/nova/rulesets/blacklist.lst }

  http-request set-header X-Nova-SRC %[src]
  http-request set-header X-Nova-Country %[src,map_ip(/etc/nova/rulesets/ip2country.map)]

  option httplog
  log 127.0.0.1:12346 local0
  capture request header Host len 64
  capture request header User-Agent len 200
  capture response header Server len 20
  capture response header Content-Type len 64
  mode http
  option forwardfor
  option http-server-close
  timeout http-request 15s
  http-response set-header Server NOVA
  http-request track-sc0 src table per_ip_rates
  http-request track-sc1 url32+src table per_ip_and_url_rates unless { path_end .css .js .png .jpeg .gif .woff .jpg }
  http-request allow if { src -f /etc/nova/rulesets/whitelist.lst }

default_backend int-be-http-451-test

backend int-be-http-451-test
 mode http
 option accept-invalid-http-response
 balance roundrobin
 server test0 52.15.129.74:443  weight 10 check rise 5 fall 3 inter 2000


backend per_ip_and_url_rates
  stick-table type binary len 8 size 1m expire 24h store http_req_rate(24h)

backend per_ip_rates
  stick-table type ip size 1m expire 24h store gpc0,gpc0_rate(30s)

Now we re-run the validation tool, to see if it passes validation:

haproxy -f /etc/haproxy/haproxy.cfg -c

We should see the following "Configuration file is valid" message:

# haproxy -f /etc/haproxy/haproxy.cfg -c
Configuration file is valid
# 

Restarting the HAProxy service

Now that we've successfully validated the configuration file, we can safely restart the HAProxy service:

sudo systemctl restart haproxy.service

Let's check the status, with:

sudo systemctl status haproxy.service

...which should output something similar to the following:

● haproxy.service - HAProxy Load Balancer
     Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2022-08-31 14:23:35 BST; 2s ago
       Docs: man:haproxy(1)
             file:/usr/share/doc/haproxy/configuration.txt.gz
    Process: 1789309 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS (code=exited, status=0/SUCCESS)
   Main PID: 1789310 (haproxy)
      Tasks: 7 (limit: 18885)
     Memory: 2.5M
     CGroup: /system.slice/haproxy.service
             ├─1789310 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock
             └─1789311 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock

Further down, we can also see our frontend and backend HAProxy services have successfully started:

Aug 31 14:23:35 haproxy[1789310]: Proxy nova-http-451 started.
Aug 31 14:23:35 haproxy[1789310]: Proxy nova-http-451 started.
Aug 31 14:23:35 haproxy[1789310]: [NOTICE] 242/142335 (1789310) : New worker #1 (1789311) forked
Aug 31 14:23:35 haproxy[1789310]: Proxy int-be-http-451-test started.
Aug 31 14:23:35 systemd[1]: Started HAProxy Load Balancer.
Aug 31 14:23:35 haproxy[1789310]: Proxy int-be-http-451-test started.
Aug 31 14:23:35 haproxy[1789310]: Proxy per_ip_and_url_rates started.
Aug 31 14:23:35 haproxy[1789310]: Proxy per_ip_and_url_rates started.
Aug 31 14:23:35 haproxy[1789310]: Proxy per_ip_rates started.
Aug 31 14:23:35 haproxy[1789310]: Proxy per_ip_rates started.

Firewall Considerations

It is also worth checking that your iptables/firewalld rules are configured correctly to allow any inbound traffic you are loadbalancing with HAProxy.

Conclusion

And that's all there is to it! As you can see, it's a very straightforward process to get a vanilla HAProxy service up and running, using a Nova configuration file.

If you're needing to transition asap please do reach out. We've helped many others over the last few weeks in exactly the same situation.

Stressed about SNAPT?

Get help with your transition