The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

block-web-flooders - Block IP addresses of web flooders using iptables

VERSION

This document describes version 0.010 of block-web-flooders (from Perl distribution App-BlockWebFlooders), released on 2019-01-29.

SYNOPSIS

This script should be run as root/sudo root, because it needs to call the iptables command to add block rules to the firewall.

First of all, create /etc/block-web-flooders.conf that contains something like this:

 whitelist_ip = 1.2.3.4
 whitelist_ip = ...

Where 1.2.3.4 is the IP address(es) that you are connecting from (you can see this from output of w command), to make sure you yourself don't get blocked. Add more lines/IP address as necessary.

When a flood is happening, try to tail your web access log file:

 # tail -f /s/example.com/syslog/https_access.2017-06-07.log

and see the patterns that you can use to discriminate the requests coming from the flooder. Since the IP address is usually random/many, you can see from other patterns e.g. requested URI, user agent. For example, if the suspicious log lines are something like this:

 93.186.253.79 - - [07/Jun/2017:00:54:23 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 51.15.41.74 - - [07/Jun/2017:00:54:25 +0000] "POST /heavy2.php HTTP/1.1" 302 - "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.38.149.5 - - [07/Jun/2017:00:54:24 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 93.186.253.79 - - [07/Jun/2017:00:54:24 +0000] "GET /heavy3.php HTTP/1.0" 200 20524 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 51.15.41.74 - - [07/Jun/2017:00:54:25 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.38.149.5 - - [07/Jun/2017:00:54:25 +0000] "GET /heavy3.php HTTP/1.0" 200 20524 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.38.149.5 - - [07/Jun/2017:00:54:25 +0000] "GET /heavy3.php HTTP/1.0" 200 20524 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 93.186.253.79 - - [07/Jun/2017:00:54:26 +0000] "POST /heavy2.php HTTP/1.1" 302 - "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 51.15.41.74 - - [07/Jun/2017:00:54:25 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.36.213.37 - - [07/Jun/2017:00:54:26 +0000] "GET /heavy3.php HTTP/1.0" 200 20524 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.36.213.37 - - [07/Jun/2017:00:54:27 +0000] "POST /heavy2.php HTTP/1.1" 302 - "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.38.149.5 - - [07/Jun/2017:00:54:26 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"
 89.36.213.37 - - [07/Jun/2017:00:54:26 +0000] "GET /heavy1.php HTTP/1.0" 200 20633 "-" "Opera/9.80 (Windows NT 6.0; U; en) Presto/2.2.0 Version/10.00"

you can add --has Presto/2.2.0 and --has /heavy since these quite accurately selects the flood requests. If you can add strings which pretty accurately single out the flood requests, you can use a lower threshold speed, e.g. --limit 5 to block IPs which has requested 5 or more in the last 5 minutes. Otherwise, if you do not have any specific --has to single out the flood, you might need to set a higher limit, e.g. --has html --limit 30 --period 60 to block IPs which have requested 30 or more requests in the last minute, or --limit 200 --period 120 to block IPs which have requested 200 or more requests in the last 2 minutes.

Feed the output of the tail command to this script:

 # tail -f /s/example.com/syslog/https_access.2017-06-07.log | block-web-flooders \
   --has Presto/2.2.0 --has-pattern '/heavy|/baz' --limit 5

or perhaps:

 # tail -f /s/example.com/syslog/https_access.2017-06-07.log | block-web-flooders \
   --limit 200 --period 120

The script will display the top IP addresses and whether an IP is being blocked, along with some statistics:

 Blocked IPs this session:  12 | Log lines:  198 | Running for: 2m13s
 Top IPs:
   89.36.213.37    (  4)
   89.38.149.5     (  2)
   93.186.253.79   (  2)
   ...
 Last messages:
   51.15.41.74 BLOCKED

While this script is running, you might also want to open something like this in another terminal (monitor incoming web requests):

 # tail -f /s/example.com/syslog/https_access.2017-06-07.log | grep /heavy

and somethins like this in yet another terminal (monitor system load and number of web server processes, this depends on the web server you use):

 # watch 'w | head -n1; echo -n "Blocked IPs total: "; iptables -nL INPUT | wc -l; echo -n "Apache processes: "; ps ax | grep apache | wc -l'

If your webserver is still maxed out by requests, you might want to tweak --limit and --period options and restart the web server.

To see the blocked IP addresses:

 # iptables -nL INPUT

As long as the script runs, IP addresses are blocked by default temporarily for 86400 seconds (or, according to the --block-period command-line option or block_period configuration). After that block period is exceeded, the IP is unblocked.

To immediately clear/unblock all the IPs:

 # iptables -F INPUT

(this is assuming the default policy of input is ACCEPT; if you have a firewall package installed, please follow the procedure for that firewall.)

To immediately unblock some IPs:

 #

DESCRIPTION

This script helps a sysadmin when there is a flood from multiple IP addresses to your website. The script works by reading web access log file, considering lines which match the specified pattern(s), then block the IP address of the requester if the speed of request from that IP exceeds a limit. The blocking is done using firewall (iptables), by default:

 # iptables -A INPUT -s <ip-address> -p tcp -m multiport --dports 80,443 -j DROP

To use this script, see the general guide in the Synopsis.

OPTIONS

  • --has=S

  • --has-pattern=REGEX

  • --lacks=S

  • --lacks-pattern=REGEX

  • --limit=N

  • --period=N

  • --whitelist-ip=IP

  • --spanel-site=NAME

  • --dry-run

TODO

Option to customize ports.

Parse timestamps from web access logs so it can also parse past lines.

IPv6 support.

Some interactivity, e.g.: reset counters, unblock some IPs, increase/decrease limit.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/App-BlockWebFlooders.

SOURCE

Source repository is at https://github.com/perlancar/perl-App-BlockWebFlooders.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=App-BlockWebFlooders

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

SEE ALSO

CSF, https://configserver.com/cp/csf.html, the more generic solution.

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019, 2018, 2017 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.