#!/usr/bin/perl -w use Sys::Syslog qw(:DEFAULT setlogsock); use Fcntl qw(:seek); # Add to firewall rules $fw_cmd = "iptables -I INPUT --source %s --jump REJECT"; # Remove from rules $unfw_cmd = "iptables -D INPUT --source %s --jump REJECT"; # How many failures... $max_failures = 3; # ...in how many seconds... $max_failures_time = 120; # ...gets you firewalled for this many seconds. $firewall_time = 3600; sub putlog ($;@) { my $_fmt = shift; $_fmt = sprintf $_fmt, @_ if (@_); syslog 'warning', $_fmt; } sub unfw ($) { putlog "Timeout, removing $_[0]\n"; system(sprintf $unfw_cmd, $_[0]); delete $added{$_[0]}; delete $evil{$_[0]}; } sub dofw ($) { putlog "Ignoring $_[0] for $firewall_time seconds."; system(sprintf $fw_cmd, $_[0]); $added{$_[0]} = time; } sub incr_evil ($) { my $host = shift; my @ev = exists($evil{$host}) ? @{$evil{$host}} : (); for (my $i = 0; $i < @ev; $i++) { if (time() - $ev[$i] >= $max_failures_time) { splice @ev, $i, 1; redo; } } push @ev, time(); if (scalar(@ev) >= $max_failures && ! exists $added{$host}) { putlog "Ignoring further attempts from $host."; dofw $host; } putlog "Noticed failed attempt #%d from %s", scalar(@ev), $host; $evil{$host} = \@ev; return scalar(@ev); } setlogsock 'unix'; do { @_ = split /\//, $0; openlog pop(@_), 'pid', 'authpriv,daemon'; }; %evil = %added = (); if ($_ = fork) { print "Child process $_\n"; exit 0; } RESTART: if (defined LOGF) { close LOGF; } if (! open(LOGF, "<", (@ARGV ? $ARGV[0] : "/var/log/auth.log"))) { die "Couldn't open logfile: $!\n"; } seek LOGF, 0, SEEK_END; putlog "Watching.\n"; # IO::Select doesn't want to play nice. $st = (stat LOGF)[7]; while (1) { if (($newst = (stat LOGF)[7]) != $st) { $st = $newst; if (! defined($_ = )) { #putlog "Whoops, logfile switch?\n"; goto RESTART; } chomp; if (/^\S+ .. ..:..:.. \S+ sshd\[\d+\]: Invalid user \S+ from (\S+)/ || /^\S+ .. ..:..:.. \S+ sshd\[\d+\]: Failed \S+ for (?:invalid user )? \S+ from (\S+)/) { incr_evil $1; } } else { sleep 1; } for $host (keys %added) { unfw $host if (time() - $added{$host} >= $firewall_time); } } END { putlog "Terminating."; }