0

I have a server with CSF and ModSecurity enabled.

I'd like to set up a rule or configuration that will automatically block (for a specified amount of time) any IP that makes incoming requests to a particular subdomain. In this instance it is "cpanel.DOMAIN.com". As "cpanel" is an invalid subdomain (it doesn't exist on that domain in question) I think it is safe to say these are bots making the request. The requests can come in many times every few seconds, so I'd like to block those bots.

I'd like to do it in a way that results in the web server (LiteSpeed, in this case) not having to deal with the requests. Currently it's trying to find the requested subdomain, and then generating a 404 error. I'd like it to be blocked before it comes to that.

One suggestion I came across, for doing something similar which required adding the following to the /usr/local/csf/bin/csfpre.sh file:

#!/bin/sh
iptables -A INPUT -p tcp --match multiport --dport 80,443 -m string --string 'THEDOMAIN' --algo bm -j DROP

Is that the best (most efficient) way to go about this? Or are there better ways? "better" and "best" simply being I'd like the requests to have the least possible impact on server resources.

Thanks very much.

inspirednz
  • 174
  • 9

2 Answers2

1

Create a virtual host for the forbidden domain. It'll want it's own directory (which could be shared between multiple forbidden domains). As the default content, use this index.php:

<?php
// contents of FORBIDDEN.YourDomain.com/index.php
// spits out one UDP packet at the web visitor's port 911
// the 911 packet is intended to be detected, processed and dropped with firewall rules
$remoteaddr = $_SERVER['REMOTE_ADDR'];
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_sendto($sock, "NOPE", 4, 0, $remoteaddr, 911);
socket_close($sock);

Now the firewall rule.

(A) first create a new ipset (timeout: 1 day = 86400 seconds)

/usr/sbin/ipset create ipset_temp_blocklist \
    hash:ip \
        family inet \
        hashsize 4096 \
        maxelem 65536 \
        timeout 86400 \
        counters \
        comment

(B) then create a new iptables rule chain

/usr/sbin/iptables -N ADD_TEMP_BAN

(optional) chain rule 1: log the offending packet

/usr/sbin/iptables -A ADD_TEMP_BAN -j NFLOG --nflog-prefix "add-temp-ban="

chain rule 2: add the offender (the destination for the '911' UDP packet) to the blocklist ipset

/usr/sbin/iptables -A ADD_TEMP_BAN -j SET --add-set ipset_temp_blocklist dst

chain rule 3: end of chain rule: just drop the UDP packet

/usr/sbin/iptables -A ADD_TEMP_BAN -j DROP

(C) now make the new rulechain be called when we see an outgoing '911' UDP packet

/usr/sbin/iptables -A OUTPUT -p udp -dport 911 -j ADD_TEMP_BAN

(D) now to use the blocklist, add a rule to drop all incoming packets to web service from IP addresses listed

/usr/sbin/iptables -A INPUT -i eth0 -p tcp -m multiport --dports 80,443 -m set --match-set ipset_temp_blocklist src -j DROP

note this last rule does the following:

  • append to the end of INPUT rule chain (you might want to put it earlier your rules)
  • only applies to packets on interface 'eth0' (you might prefer 'all' or some other interface)
  • only protocol tcp ('-p all' would block everything, pings, etc etc)
  • only tcp packets going to ports 80 and 443 (you could add 8080 if you use that, etc)
  • only packets whose source is in the blocklist
  • DROP the packet.

You can list the blocked IPs like this:

/usr/sbin/ipset list ipset_temp_blocklist 

    Name: ipset_temp_blocklist
    Type: hash:ip
    Revision: 4
    Header: family inet hashsize 1024 maxelem 65536 timeout 864000 counters
    Size in memory: 123880
    References: 2
    Number of entries: 1331
    Members:
    xx.35.112.155 timeout 528177 packets 4 bytes 240
    xx.199.52.104 timeout 681175 packets 4 bytes 240
    xx.230.7.65 timeout 490347 packets 4 bytes 240
    xx.63.11.235 timeout 782713 packets 5 bytes 300
    xx.159.102.65 timeout 351110 packets 1 bytes 40
    xx.206.244.232 timeout 837758 packets 4 bytes 240
    xx.162.105.161 timeout 490665 packets 3 bytes 180

And see the activity of the iptables chain like this (the numbers are [packets:bytes]):

/usr/sbin/iptables-save -c | grep -E "ADD_TEMP_BAN|ipset_temp_blocklist"

    :ADD_TEMP_BAN - [0:0]
    [1333:73200] -A OUTPUT -p udp -dport 911 -j ADD_TEMP_BAN
    [1333:73200] -A ADD_TEMP_BAN -j NFLOG --nflog-prefix  "add-temp-ban="
    [1333:73200] -A ADD_TEMP_BAN -j SET --add-set ipset_temp_blocklist dst
    [1333:73200] -A ADD_TEMP_BAN -j DROP
    [5187:305169] -A INPUT -i eth0 -p tcp -m multiport --dports 80,443 -m set --match-set ipset_temp_blocklist src -j DROP

Adjust to taste.

jmullee
  • 208
  • 1
  • 7
0

This kind of filtering causes some bad side-effects:

It does not terminate TCP connections cleanly -> clients will try to re-send the requests, and time out eventually. Server side keeps the connection open until some timeout is hit. This can lead to exhaustion of TCP sockets and cause a DoS.

For port 80, it matches any packet where poayload is THEDOMAIN. Client requests might not contain this string directly, but if it does, it is hard to debug why that particular request fails.

For port 443, it is possible that some part of communication stream matches THEDOMAIN at some point over time. Since the encryption key of client - server connection is negotiated for every connection separately, this situation will eventually occur. If it is acceptable that a valid request fails occasionally, then it isn't a problem.

You need to make sure that the unwanted subdomain is not listed in your DNS. If it is listed in some other DNS, the only clean approach is to let your webserver process the requests.

Processing of these "extra" requests doesn't take much resources.

Tero Kilkanen
  • 34,499
  • 3
  • 38
  • 58
  • Thanks. That makes sense. I've asked the domain owner to remove the unneeded DNS entries from their domain name. Regarding server impact ... if there's hundreds coming through per minute, is that still of no significant consequence? – inspirednz Aug 07 '22 at 04:14