#!/usr/local/bin/perl
use strict;
use warnings;
my %possibleDenyIPs = ();
my %alreadyDeniedIPs = ();
my $sleepSeconds = 3;
my $minutes = 1; # failed logins within this number of mi
+nutes
my $failCriteria = 9; # number of failed logins within $minute
+s minutes
my @tailLines = ();
while(1)
{
my $currentTime = pastTime();
# First we tail the last 100 lines of /var/log/secure and then grep
+ those lines for failed password.
@tailLines = `tail -n 100 /var/log/secure | grep -i "failed passwor
+d"`;
my $count = @tailLines;
# only need to check if the number of lines with failed password i
+s greater than $failCriteria
if($count > $failCriteria )
{
# use those lines to get the IPs with failed passwords.
my $pastTime = pastTime($minutes);
%possibleDenyIPs = getPossibleDenyIPs($currentTime, $pastTime,
+ @tailLines);
# Now we read the existing hosts.deny file for IP address alre
+ady in the file.
# We do NOT want duplicate entries in the /etc/hosts.deny file.
%alreadyDeniedIPs = getAlreadyDeniedIPs();
# Finally we write the IP to /etc/hosts.deny, excluding any IP
+already in the file.
writeHostsDenyFile($failCriteria, %possibleDenyIPs);
#*** **********************************************************
+*******************************
}
# clear all the lists
@tailLines = ();
%possibleDenyIPs=();
%alreadyDeniedIPs=();
# run every $sleepSeconds
sleep($sleepSeconds);
}
######################################################################
+###############################################
sub getPossibleDenyIPs
{
my $currentTime = shift;
my $pastTime = shift;
my @tailLines = @_;
my %possibleDenyIPs = ();
for(my $index=0; $index<@tailLines; $index++)
{
if($tailLines[$index]=~/failed password/io)
{
chomp($tailLines[$index]);
print "\nLine$index***$tailLines[$index]***\n";
my @line = split /:/,$tailLines[$index];
#showArray(@line);
my $seconds = (split /\s/, $line[2])[0];
# Needs to be a numeric date versus an alpha-numeric d
+ate.
# Necessary so that comparisons between line date time
+ and past time work.
my $lineTime = lineDateTime($line[0]);
my $lineDateTime = $lineTime . $line[1] . $seconds;
print "lineDateTime: ***$lineDateTime***\n";
print "currentTime: ***$currentTime***\n";
print "pastDateTime: ***$pastTime***\n";
#<STDIN>; # crite
+ria: (1) must be failed login attempt
if($lineDateTime gt $pastTime) #
+ (2) login is within time limit
{ # i.e,
+lineDateTime after pastTime
#*************************************************
+***********************************
#my $IP = old_getIP($line[3]);
my $IP = getIP($tailLines[$index]);
print "Possible deny IP:***$IP***\n";
#*************************************************
+************************************
$possibleDenyIPs{$IP}++; # These are
+POSSIBLE deny IP's
# use the IP
+ as a key to the hash and keep track of failed login attempts
}
}
}
return %possibleDenyIPs;
}
sub getIP
{
my $line = $_[0];
if($line =~/(\d{1,3}\.){3}\d{1,3}/)
{
# $&: Perl internal variable contains the pattern that wa
+s matched
return $&;
}
}
sub getAlreadyDeniedIPs
{
my $hostsDeny = "//etc//hosts.deny";
my %deniedIPs = ();
# We read the existing hosts.deny file for IP addresses already
+in the file.
# We do not want duplicate entries in the /etc/hosts.deny file.
if(-e $hostsDeny) # if the file exists
{
unless(open(INDENY, "<$hostsDeny")) # readfile
{
warn "Can't create\\open file $hostsDeny : $!\n";
die;
}
while(<INDENY>) # get the IP's already in hosts.deny
{
my $line = $_;
#print "line:***$line***\n";
if($line =~/^(\#)/io) {next;} # skip comment lines
+ in /etc/hosts.deny, line starts with #.
else
{
my $IP = getIP($line); # ************************
+***************
print "Already denied IP:***$IP***\n";
if($IP) { $deniedIPs{$IP}++; } # use the IP as a ke
+y to the hash and set value to TRUE (1 or greater)
}
}
close(INDENY);
}
else
{ print "$hostsDeny does not exist\n";}
return %deniedIPs;
}
sub writeHostsDenyFile
{
my $failCriteria = shift;
my %possibleDenyIPs = @_;
my $hostsDeny = "//etc//hosts.deny";
my $newDenyIPsFile = "//var//log//newDenyIPs";
if(-e $hostsDeny) # if the file /etc/hosts.deny already exists
+ append to it
{
if( open(INDENY, ">>$hostsDeny") ) { }
+# append to file /etc/hosts.deny
else { warn "Can't create\\o
+pen file $hostsDeny: $!\n";}
}
else # if the file does not exist, create and writ
+e to it
{
if( open(INDENY, ">$hostsDeny") ) { }
+ # create and write to file /etc/hosts.deny
else { warn "Can't create\\op
+en file $hostsDeny: $!\n";}
}
# this file is strictly not necessary since the /etc/hosts.deny fi
+le will also contain the denied IP's
if(-e $newDenyIPsFile) # if the file /var/log/newDenyIPs alrea
+dy exists
{
if( open(INF, ">>$newDenyIPsFile") ) { }
+ # append to file /var/log/newDenyIPs
else { warn "Can't create\
+\open file $newDenyIPsFile: $!\n";}
}
else
{
if( open(INF, ">>$newDenyIPsFile") ) { }
+ # create and write to file /var/log/newDenyIPs
else { warn "Can't create\\ope
+n file $newDenyIPsFile: $!\n";}
}
my $comment = commentDateTime();
foreach my $key (sort keys %possibleDenyIPs)
{
# If the IP has already been denied,
# i.e., it is already in /etc/hosts.deny, do nothing.
if($alreadyDeniedIPs{$key}) {next;}
else
{
# If the IP has $failCriteria or more failed login attempt
+s,
# write a new rule in the /etc/hosts.deny file for the
+IP.
if( $possibleDenyIPs{$key} > $failCriteria)
{
print INDENY "sshd : $key $comment\n";
print INF "sshd : $key $comment failed logins: $p
+ossibleDenyIPs{$key}\n";
}
}
}
close(INDENY);
close(INF);
}
sub pastTime # used to get current time and past time for compa
+risons
{
my $secondsPast = 0;
if($_[0]) { $secondsPast = $_[0]*60; }
my $Time = time() - $secondsPast;
my ($sec, $min, $hour, $monthday, $month, $year, $wday, $yday, $is
+dst) = localtime ($Time);
#my $thisday = qw(Sun Mon Tue Wed Thur Fri Sat)[(localtime)[6]];
#$mon = qw(Jan Feb Mar Apr May June July Aug Sept Oct Nov Dec)[(lo
+caltime)[4]];
# month: Jan=0, Feb=1, Mar=3, etc.
$year = $year + 1900;
$month++;
if($monthday < 10) {$monthday = '0' . $monthday};
if($hour < 10) {$hour = '0' . $hour};
if($min < 10) {$min = '0' . $min};
if($sec < 10) {$sec = '0' . $sec};
if($month <10) {$month = '0' . $month};
#my $timeString = "$mon ". "$monthday " . "$hour:$min:$sec";
my $timeString = $month . $monthday . "$hour$min$sec";
return $timeString;
}
sub lineDateTime
{
# date will be like May 19 10:07:25
my $dateTimeString = shift;
# split on space
my ($month, $monthday, $time) = split /\s/, $dateTimeString;
# Need 2 digit date for comparisons like 7 10 and 7 8 to work prop
+erly
if($monthday && $monthday < 10) # the date
{
$monthday = '0' . $monthday;
}
# split on colon and then join
# want time to be formatted as hhmmss versus hh:mm:ss
my @timeString = split /:/, $time;
for(my $index=0; $index<@timeString; $index++)
{
if($timeString[$index] < 10)
{
$timeString[$index] = '0' . $timeString[$index];
}
}
$time = join "", @timeString);
# want numeric month
if($month =~ /^(jan)/io) {$month = '01';}
if($month =~ /^(feb)/io) {$month = '02';}
if($month =~ /^(mar)/io) {$month = '03';}
if($month =~ /^(apr)/io) {$month = '04';}
if($month =~ /^(may)/io) {$month = '05';}
if($month =~ /^(jun)/io) {$month = '06';}
if($month =~ /^(jul)/io) {$month = '07';}
if($month =~ /^(aug)/io) {$month = '08';}
if($month =~ /^(sep)/io) {$month = '09';}
if($month =~ /^(oct)/io) {$month = '10';}
if($month =~ /^(nov)/io) {$month = '11';}
if($month =~ /^(dec)/io) {$month = '12';}
$dateTimeString = join "", $month, $monthday, $time;
return $dateTimeString;
}
sub commentDateTime
{
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = l
+ocaltime();
#my $thisday = qw(Sun Mon Tue Wed Thur Fri Sat)[(localtime)[6]];
$mon = qw(Jan Feb Mar Apr May June July Aug Sept Oct Nov Dec)[(local
+time)[4]];
# month: Jan=0, Feb=1, Mar=3, etc.
#**************mon++; if($mon < 10) {$mon = '0' . $mon};
$year = $year + 1900;
#$mon++;
#print "comment month = $mon\n";
if($mday < 10) {$mday = '0' . $mday};
if($hour < 10) {$hour = '0' . $hour};
if($min < 10) {$min = '0' . $min};
if($sec < 10) {$sec = '0' . $sec};
#if($mon < 10) {$mon = '0' . $mon};
my $commentDateTime = "# $year" . "$mon". "$mday " . "$hour:$min:$s
+ec";
return $commentDateTime;
}
|