ipv6 banning
-
My wordpress sites are ip6 enabled, but fail2ban currently is not. I wrote a perl script that monitors the syslog file from wp-fail2ban and inserts banned ips into ipsets that my firewall drops. If anyone wants a copy to play with, let me know.
Viewing 2 replies - 1 through 2 (of 2 total)
-
Hi muskokaslowpitcher,
Brilliant! this is exactly what I’m looking for. Would you be so kind sharing the script here so I and others can play (work, that is) with it. Thanks so much in advance. Have a good one:)
Ok, here it is. It was adapted from a script used to gather mail statistics.
#!/usr/bin/perl -w # wp_ip6_bans -- # copyright (c) 2015 Jeremy Baker <[email protected]> # copyright (c) 2013 KokelNET # copyright (c) 2013 Tobias Hachmer <[email protected]> # copyright (c) 2000-2007 ETH Zurich # copyright (c) 2000-2007 David Schweikert <[email protected]> # released under the GNU General Public License ##################################################################### ##################################################################### ##################################################################### use strict; use File::Tail; use Getopt::Long; use POSIX 'setsid'; use NetAddr::IP; use Date::Parse; use Data::Validate::IP qw(is_ipv6); use Data::Validate::IP qw(is_ipv4); use Email::Simple; use Email::Sender::Simple qw(sendmail); use Email::Simple::Creator; use BerkeleyDB; use BerkeleyDB::Hash; my $VERSION = "0.3.1"; # config my $daemon_logfile = '/var/log/wp_ip6_bans.log'; my $daemon_pidfile = '/var/run/wp_ip6_bans.pid'; my $ban_limit = 3; my $ban_search_time = 600; # search window - 10 min my $ban_time = 3600; # 1 hour my $rec_ban_limit = 3; my $rec_ban_search_time = 86400; # search window - 1 day my $rec_ban_time = 604800; # 7 days my $ban_grace_time = 30; # depending on the speed of logging and reading the logs, sometimes there is a lapse between a ban and activity ceasing my $notification_email ='[email protected]'; my $ipv4_whitelist = ''; my $ipv6_whitelist = ''; # global variables my $logfile = '/var/log/wordpress'; my $db = new BerkeleyDB::Hash( -Filename => '/var/lib/wp_ip6_bans.dbm', -Flags => DB_CREATE ) or die "Cannot open file: $!"; my %opt = (); # prototypes sub daemonize(); sub process_line($); sub usage { print "usage: wp_ip6_bans [*options*]\n\n"; print " -h, --help display this help and exit\n"; print " -v, --verbose be verbose about what you do\n"; print " -V, --version output version information and exit\n"; # print " -c, --cat causes the logfile to be only read and not monitored\n"; print " -l, --logfile f monitor logfile f instead of /var/log/wordpress\n"; print " -t, --logtype t set logfile's type (default: syslog)\n"; print " -d, --daemon start in the background\n"; print " -m, --mail send email for each ban\n"; print " --daemon-pid=FILE write PID to FILE instead of /var/run/wp_ip6_bans.pid\n"; print " --daemon-log=FILE write verbose-log to FILE instead of /var/log/wp_ip6_bans.log\n"; exit; } sub main { Getopt::Long::Configure('no_ignore_case'); GetOptions(\%opt, 'help|h', 'cat|c', 'logfile|l=s', 'logtype|t=s', 'version|V', 'year|y=i', 'verbose|v', 'daemon|d!', 'daemon_pid|daemon-pid=s', 'mail|m', 'daemon_log|daemon-log=s' ) or exit(1); usage if $opt{help}; if($opt{version}) { print "wp_ip6_bans $VERSION by jab\@mbcs.ca\n"; exit; } $daemon_pidfile = $opt{daemon_pid} if defined $opt{daemon_pid}; $daemon_logfile = $opt{daemon_log} if defined $opt{daemon_log}; daemonize if $opt{daemon}; my $logfile = defined $opt{logfile} ? $opt{logfile} : '/var/log/wordpress'; my $file; my $sl; if($opt{cat}) { $file = $logfile; } else { $file = File::Tail->new(name=>$logfile, maxinterval=>2); } while (defined($sl=$file->read)) { if(($sl =~ /Authentication failure for .* from /)or($sl =~ /Blocked user enumeration attempt from /)) { process_line($sl); } } } sub daemonize() { open STDIN, '/dev/null' or die "wp_ip6_bans: can't read /dev/null: $!"; if($opt{verbose}) { open STDOUT, ">>$daemon_logfile" or die "wp_ip6_bans: can't write to $daemon_logfile: $!"; } else { open STDOUT, '>/dev/null' or die "wp_ip6_bans: can't write to /dev/null: $!"; } defined(my $pid = fork) or die "wp_ip6_bans: can't fork: $!"; if($pid) { # parent open PIDFILE, ">$daemon_pidfile" or die "wp_ip6_bans: can't write to $daemon_pidfile: $!\n"; print PIDFILE "$pid\n"; close(PIDFILE); exit; } # child setsid or die "wp_ip6_bans: can't start a new session: $!"; open STDERR, '>&STDOUT' or die "wp_ip6_bans: can't dup stdout: $!"; } sub process_line($) { my $line = shift; my $stime=substr $line, 0 , 19; my $time=str2time(substr $line, 0, 19); my $data; my $exists=0; my $attempts; my $bans; my $ban_it=0; my $first_time; my $first_ban_time; my $last_ban_time; my $btime = $ban_time; my $ipset = ''; my $log_message =''; my $log_message2 = 'not blocked'; my @values = split(' ',$line); my $ip = $values[-1]; if(is_ipv6($ip)) { $ipset = 'F2BLIST6'; # this should be added to a configuration variable } elsif(is_ipv4($ip)) { $ipset = 'F2BLIST'; # this should be added to a configuration variable } else { return; } # database format $ip, $attempts:$bans:$first_time:$first_ban_time:$last_ban_time # find out if $ip is in database, and load variables if ($db->db_get( $ip, $data) == 0) { # record exists ( $attempts, $bans, $first_time, $first_ban_time, $last_ban_time ) = split ':', $data; if (($time-$last_ban_time) < $ban_grace_time) { print "at $time $ip ignored because it was banned at $last_ban_time\n" if $opt{verbose}; return; } $exists = 1; } # I immediately ban any attempts to login as admin, or use user enumeration if(($line =~ /Authentication failure for admin from /)or($line =~ /Blocked user enumeration attempt from /)) { $ban_it=1; $log_message = 'blocked after 1 attempt'; } elsif($line =~ /Authentication failure for .* from /) { if ($exists == 1) { # record exists if (($time-$first_time) < $ban_search_time) { if ($attempts < ($ban_limit -1)) { # increment counter $attempts = $attempts + 1; $log_message = 'incremented attempts'; } else { # ban ip, $ban_it=1; $log_message = 'blocked after repeated attempts'; } } else { # reset counter to 1, reset time $attempts = 1; $first_time = $time; $log_message = 'reset attempts and time'; } } else { # create record $attempts = 1; $bans = 0; $first_time = $time; $first_ban_time = 0; $last_ban_time = 0; $log_message = 'record created'; } } if ($ban_it == 1) { $attempts = 0; $first_time = 0; $last_ban_time = $time; if ($exists == 1) { # record exists if (($time-$first_ban_time) < $rec_ban_search_time) { # within recidive search window if ($bans < ($rec_ban_limit -1)) { # increment and ban normally $bans = $bans + 1; $log_message2 = 'bans incremented'; } else { # ban recidively, $btime=$rec_ban_time; $bans = 0; $first_ban_time = 0; $log_message2 = 'blocked recidively and reset'; } } else { # outside recidive window - reset counter to 1, reset time, ban normally $bans = 1; $first_ban_time = $time; $log_message2 = 'ban count reset'; } } else { # new record $bans = 1; $first_ban_time = $time; $log_message2 = 'record created'; } # the actual ban command system("/usr/sbin/ipset add $ipset $ip timeout $btime -exist"); # send an optional email if ($opt{mail}) { my $email = Email::Simple->create( header => [ To => '"your name here" <[email protected]>', From => '"Wordpress Banning Daemon" <[email protected]>', Subject => "$ip banned for $btime seconds", ], body => "A firewall ban was triggered by the following log line \n$line", ); sendmail($email); } } # we saw a line, so update the database $db->db_put ( $ip, join(':', $attempts, $bans, $first_time, $first_ban_time, $last_ban_time) ); print "at $stime $ip $attempts $bans $first_time $first_ban_time $last_ban_time $log_message and $log_message2\n" if $opt{verbose}; } main; __END__ =head1 NAME wp_ip6_bans.pl - script to ban brute force attempts on wordpress at the firewall =head1 SYNOPSIS B<wp_ip6_bans.pl> [I<options>...] --man show man-page and exit -h, --help display this help and exit -v, --verbose be verbose about what you do -V, --version output version information and exit -c, --cat causes the logfile to be only read and not monitored -l, --logfile f monitor logfile f instead of /var/log/wordpress -t, --logtype t set logfile's type (default: syslog) -d, --daemon start in the background -m, --mail send email for each ban --daemon-pid=FILE write PID to FILE instead of /var/run/shoregraph.pid --daemon-log=FILE write verbose-log to FILE instead of /var/log/shoregraph.log =head1 DESCRIPTION This script does parse syslog and bans IPs that make too many attempts to log in to wordpress =head2 Log-Types The following types can be given to --logtype: =over 10 =item syslog Traditional "syslog" (default) =item metalog Metalog (see https://metalog.sourceforge.net/) =back =head1 COPYRIGHT Copyright (c) 2015 Jeremy Baker <[email protected]> Copyright (c) 2013 KokelNET Copyright (c) 2013 Tobias Hachmer <[email protected]> Copyright (c) 2000-2007 by ETH Zurich Copyright (c) 2000-2007 by David Schweikert =head1 LICENSE This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. =head1 AUTHOR S<Jeremy Baker E<lt>[email protected]<gt>> =cut # vi: sw=8
Viewing 2 replies - 1 through 2 (of 2 total)
- The topic ‘ipv6 banning’ is closed to new replies.