Hi all!
we are currently exploring the different options available within HAProxy to
protect our systems from attacks and accidental overloads. We got the basics
figured out by following the very well written introduction to stick tables
[1]. What we ended up with is something like this:
stick-table type ip size 10m expire 10s store http_req_rate(10s)
tcp-request content track-sc0 src
http-request deny status 429 content-type "text/plain" string "429: Too Many
Requests" if { sc_http_req_rate(0) gt 5 }
This allows us to protect the systems behind HAProxy very well by not letting
request through that are above our defined limit. However, we realised that the
load on HAProxy per blocked request is very similar to the load per "allowed"
request. The idea we had was to block earlier if we know that an IP already
made a lot of requests. This would reduce the load on HAProxy since it doesn't
have to perform a TLS handshake and inspect the request before blocking it. We
expected to be able to just add the following line to the config and block any
new connections:
tcp-request connection reject if { sc_http_req_rate(0) gt 10 }
The idea being that we "softly" limit IPs that exceed our limits a bit, but we
have the possibility to completely lock out a certain IP if it continues to
generate a high number of requests.
However, this does not work at all and we do not understand why. Can you please
explain to us why this doesn't work and if there is a different way to achieve
this goal?
Blocking based on the connection rate does work as described in the
documentation [2], which led us to believe that the client IP is available and
we should be able to reject the connection based on it.
Thanks in advance!
Best Regards,
Max
[1] https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/
[2]
https://cbonte.github.io/haproxy-dconv/2.5/configuration.html#4-tcp-request%20connection
PS: We are aware that open connections could keep sending requests and loading
HAProxy even if the connection rejection rule kicks in, this could be solved by
adding a `http-request reject if { sc_http_req_rate(0) gt 10 }` which closes
the connection.