In this post, we will go through several methods of securing an HAProxy setup as we talked previous on how to secure nginx. We will discuss Access Control Lists (ACLs), rate limiting, and GeoIP blocking. We will also look at only allowing traffic from specific networks such as Cloudflare.

1. Access Control Lists (ACLs)

HAProxy uses ACLs to test some condition and perform an action based on the test result. Here’s an example of allowing a specific IP address:

frontend http_front
  bind *:80
  acl allowed_ip src 192.168.1.100
  http-request allow if allowed_ip
  http-request deny if !allowed_ip

In the example above, HAProxy allows requests only from the IP address 192.168.1.100 and denies the rest.

2. Rate Limiting

Rate limiting can be achieved in HAProxy using the stick-tables. Here is an example:

frontend http_front
  bind *:80
  stick-table type ip size 200k expire 10s store http_req_rate(10s)
  http-request track-sc0 src
  acl rate_too_high sc_http_req_rate(0) gt 10
  http-request deny deny_status 429 if rate_too_high

This tracks HTTP requests from each source over ten seconds and returns 429 Too Many Requests above the threshold. The older tcp-request connection example tracked connection state rather than the HTTP request counter it later queried. HAProxy stick-table tutorial

3. Allow only Cloudflare IPs

To only allow traffic from Cloudflare, create ACLs for Cloudflare’s IP ranges:

frontend http_front
  bind *:80
  acl from_cloudflare src -f /etc/haproxy/cloudflare_ips.lst
  http-request deny unless from_cloudflare
  http-request set-src req.hdr_ip(CF-Connecting-IP) if from_cloudflare

/etc/haproxy/cloudflare_ips.lst contains Cloudflare’s published ranges, one per line. I validate the immediate TCP peer before trusting CF-Connecting-IP; otherwise a direct client could forge the header. I refresh the list from Cloudflare’s official IP ranges.

HAProxy and Fail2Ban

Fail2Ban can be used to monitor HAProxy logs for suspicious activity. The filter below assumes HAProxy’s standard HTTP log format:

defaults
  option httplog
  1. Install Fail2Ban, if it’s not already installed.

  2. Create a new filter for HAProxy. Create a new file at /etc/fail2ban/filter.d/haproxy-http-auth.conf with the following content:

    [Definition]
    failregex = ^.*haproxy\[\d+\]: <HOST>:\d+ \[[^]]+\] \S+ \S+/\S+ \S+ 401 \S+ .* "[A-Z]+ [^"]+ HTTP/\d(?:\.\d)?"$
    
  3. Create a new jail in the /etc/fail2ban/jail.local file:

    [haproxy-http-auth]
    enabled = true
    filter = haproxy-http-auth
    logpath = /var/log/haproxy.log
    maxretry = 3
    bantime = 3600
    port = http,https
    
  4. Restart Fail2Ban to apply the new configuration.

I test the filter against real lines from the same option httplog output before enabling the jail:

sudo fail2ban-regex /var/log/haproxy.log /etc/fail2ban/filter.d/haproxy-http-auth.conf
sudo haproxy -c -f /etc/haproxy/haproxy.cfg

Fail2Ban should now monitor your HAProxy logs and block IPs that repeatedly fail authentication.

Adjusting Timeouts

Timeouts prevent slow or stalled connections from living forever, but these numbers are workload examples rather than universal security values. A file upload, streaming response, or slow upstream may need much longer limits.

There are several directives related to timeout settings:

    timeout client 5s
    timeout http-request 5s
    timeout http-keep-alive 5s
    timeout server 5s
  • timeout client: Similar to client_header_timeout in NGINX, it sets the maximum inactivity time on the client side.
  • timeout http-request: Maximum allowed time to wait for a complete HTTP request.
  • timeout http-keep-alive: Maximum allowed time to wait for a new HTTP request to appear.
  • timeout server: Similar to send_timeout in NGINX, it sets the maximum inactivity time on the server side.


Buy Me a Coffee