App-BlockWebFlooders
view release on metacpan or search on metacpan
script/block-web-flooders view on Meta::CPAN
} else {
return unless $Blocked{$ip};
}
system(
{
die => ($which eq 'block' ? 1:0),
dry_run => $Opts{dry_run},
(capture_stderr => \my $stderr) x ($which eq 'block' ? 0:1),
},
"iptables", ($which eq 'block' ? "-A" : "-D"), "INPUT", "-s", $ip,
"-p", "tcp", "-m", "multiport", "--dports", "80,443",
"-j", "DROP",
);
my $now = time();
if ($which eq 'block') {
unshift @Messages, "$ip BLOCKED".($Opts{dry_run} ? " (dry-run)" : "")
if $update_messages;
$Dbh->do("INSERT OR IGNORE INTO blocked (ip,ctime) VALUES (?,?)", {}, $ip, $now);
$Blocked{$ip} = time();
} else {
script/block-web-flooders view on Meta::CPAN
my $last_check_spanel_log_time;
my ($spanel_http_log_name, $spanel_https_log_name);
my $last_update_output_time;
my $last_unblock_time;
my $last_reload_data_time;
my $num_lines = 0;
_init();
local *INPUT;
if (defined $Opts{spanel_site}) {
require Tie::Handle::TailSwitch;
my $dir = "/s/$Opts{spanel_site}/syslog";
tie *INPUT, 'Tie::Handle::TailSwitch', (
globs => ["$dir/https_access.*.log", "$dir/http_access.*.log"],
);
} else {
*INPUT = \*STDIN;
}
LINE:
while (1) {
my $line = <INPUT>;
if (!defined($line) || !length($line)) {
sleep 0.5;
next;
}
my $now = time();
$num_lines++;
chomp $line;
$line =~ /\A($RE{ipv4})\s/ or do {
warn "$PROG: Line '$line': Can't parse IP address, skipped\n";
next;
};
my $ip = $1;
next if $Blocked{$ip};
OUTPUT:
{
last unless !$last_update_output_time ||
$last_update_output_time <= $now-2;
print "\e[2J\e[;H"; # clear screen + put cursor at top (0,0)
printf "Blocked IPs: %s%4d%s | Log lines: %s%6d%s | Running for: %s%s%s\n",
color('bold'), (scalar keys %Blocked), color('reset'),
color('bold'), $num_lines, color('reset'),
color('bold'), Time::Duration::concise(Time::Duration::duration($now-$^T, 2)), color('reset');
$last_update_output_time = $now;
printf "Top IPs:\n";
script/block-web-flooders view on Meta::CPAN
for my $ip (sort { scalar(@{ $Ips{$b} }) <=> scalar(@{ $Ips{$a} }) } keys %Ips) {
last if $i++ >= 10;
printf " %15s (%4d)\n", $ip, scalar(@{ $Ips{$ip} });
}
printf "Last messages:\n";
$i = 0;
for my $msg (@Messages) {
last if $i++ >= 5;
print " $msg\n";
}
} # OUTPUT
UNBLOCK:
{
last unless !$last_unblock_time ||
$last_unblock_time <= $now-60;
for (keys %Blocked) {
next unless $Blocked{$_} < $now - $Opts{block_period};
unblock_ip($_);
}
$last_unblock_time = $now;
script/block-web-flooders view on Meta::CPAN
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:
# 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:
#
=head1 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 (L<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.
=head1 OPTIONS
=over
=item * --has=S
=item * --has-pattern=REGEX
( run in 0.362 second using v1.01-cache-2.11-cpan-4e96b696675 )