On Fri, Feb 24, 2023 at 05:39:13AM +0000, Robin H. Johnson wrote:
> On Thu, Feb 23, 2023 at 06:48:14PM -0700, Bryan Arenal wrote:
> > Hi there,
> > 
> > I'm seeing some traffic from what appears to be bad actors and am
> > wanting to block them.  I see this in the existing config but being
> > new to haproxy, it doesn't seem like it's configured correctly but I'm
> > not sure:
> > 
> > frontend main
> >         bind :80
> >         acl bad_ip src
> >         acl bad_ip_proxy hdr_ip(X-Forwarded-For)
> Off the top of my head, this should probably be:
> acl bad_ip src -f /etc/haproxy/blocklist.lst
> acl bad_ip_proxy hdr_ip(X-Forwarded-For) -f /etc/haproxy/blocklist.lst
> 
> >         tcp-request connection reject if bad_ip || bad_ip_proxy
> I'm not sure offhand about the processing order for the header case.
> 
> You might need BOTH:
> tcp-request connection reject if bad_ip || bad_ip_proxy
> http-request connection reject if bad_ip || bad_ip_proxy

In fact the tcp-request rule will not consider the bad_ip_proxy ACL since
it relies on header extraction that is not possible at this step. Indeed,
You'd need to deal with this using the http-request rule. I would also
suggest that if the IP is blocked at the TCP level then it's not needed
to evaluate it again at the HTTP layer. This would give:

  tcp-request connection reject if bad_ip
  http-request reject if bad_ip_proxy

> Depending on the scale of the traffic, the one problem you'll have here is
> that HAProxy still has to process the problematic requests.

In fact it depends. For example if it's a POST with a heavy body, a "deny"
rule will return a 403 and will drain the rest of the body in order to try
to recover the connection, but a "reject" rule will instantly break the
connection, which instantly stops any processing.

> In that case I suggest writing a feedback loop that adds the bad IP to an 
> ipset
> set, to block the traffic before it gets to HAProxy, for some period of time.
> The trigger for the loop can either be a tail on the logfile, or using some
> variation of the set* functionality (set-acl, set-map, set-mark) and exporting
> the data to ipset.

I used to proceed like this a very long time ago, and found that it was
not always convenient due to the lack of observability (i.e. "is that rule
still needed or has the attack stopped?"). Given the performance of most
machines nowadays, I *tend* to prefer only blocking using the ACLs in TCP
rules (that's still very fast). However you're right, sometimes it can be
required to block IPs at the packet level, particularly for multi-protocol
attacks that span over many ports.

By the way, if attacks come from a small to medium sized set of clients,
the "tarpit" and "silent-drop" actions can be more effective because they
consume resources on the client by keeping the connection open. But *do not*
do that if there is a firewall or NAT box between the net and your LB or
it might not find this funny. For silent-drop there's a new "rst-ttl"
option that allows to send a reset with a limited TTL that only the
firewall/NAT box will see. This one might be considered as an option.

Willy

Reply via email to