App-BlockWebFlooders
view release on metacpan or search on metacpan
script/block-web-flooders view on Meta::CPAN
if ($action eq 'block') {
action_block();
} elsif ($action eq 'unblock') {
action_unblock();
} elsif ($action eq 'list_blocked') {
action_list_blocked();
} elsif ($action eq 'unblock_all') {
action_unblock_all();
} elsif ($action eq 'run') {
require Sys::RunAlone::Flexible;
Sys::RunAlone::Flexible::lock();
action_run();
} else {
die "$PROG: Unknown action '$action'\n";
}
# PODNAME: block-web-flooders
# ABSTRACT: Block IP addresses of web flooders using iptables
__END__
=pod
=encoding UTF-8
=head1 NAME
block-web-flooders - Block IP addresses of web flooders using iptables
=head1 VERSION
This document describes version 0.010 of block-web-flooders (from Perl distribution App-BlockWebFlooders), released on 2019-01-29.
=head1 SYNOPSIS
This script should be run as root/sudo root, because it needs to call the
L<iptables> command to add block rules to the firewall.
First of all, create F</etc/block-web-flooders.conf> that contains something
like this:
whitelist_ip = 1.2.3.4
whitelist_ip = ...
Where C<1.2.3.4> is the IP address(es) that you are connecting from (you can see
this from output of L<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 C<--has Presto/2.2.0> and C<--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. C<--limit 5> to block IPs which has requested 5 or more in the last 5
minutes. Otherwise, if you do not have any specific C<--has> to single out the
flood, you might need to set a higher limit, e.g. C<--has html --limit 30
--period 60> to block IPs which have requested 30 or more requests in the last
minute, or C<--limit 200 --period 120> to block IPs which have requested 200 or
more requests in the last 2 minutes.
Feed the output of the C<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
C<--limit> and C<--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:
( run in 1.510 second using v1.01-cache-2.11-cpan-39bf76dae61 )