#!/usr/bin/perl # mailmgr (C) 2007, Julian Haight, All Rights reserved under GPL license: # http://www.gnu.org/licenses/gpl.txt # this is a script which tails a file *or* checks the output of a command line # to find IPs to block. Adapted from a similar script to manage mail-flow # "mailmanager". # both scripts are available from http://www.julianhaight.com/ use strict; use Net::hostent; use Socket; my($OCT) = '(?:25[012345]|2[0-4]\d|1?\d\d?)'; my($IP) = $OCT . '\.' . $OCT . '\.' . $OCT . '\.' . $OCT; my($MYIP) = '216.127.43.94'; # This example looks for syn-flood with non-forged source IP my($LOG) = "netstat -n |grep $MYIP:80 | grep SYN_RECV|sed 's/.*$MYIP:80*//'|sed 's/:[0-9]* */ /'|sort -u|"; my($REASONS) = "($IP) ([a-zA-Z_-]+)"; # this looks for clients doing bad things to our apache webserver #my($LOG) = '/var/log/apache2/error_log'; # my($REASONS) = '\[client ([\d\.]+)\] (Invalid URI|Invalid method|request failed|File does not exist:.*\.\.)' my($IPTABLES) = '/usr/sbin/iptables'; my(%buffer, %stats); removeBlocks(); taillog(); sub taillog { my($offset, $name, $val, $line, $params, $ip, $ctrladdr, $qid, $conn, $reason); $offset = (-s $LOG); # Don't start at begining, go to end # $offset=0; while (1==1) { sleep(1); if (-e $LOG) { if ((-s $LOG) < $offset) { print "Log shrunk, resetting..\n"; $offset = 0; } open(TAIL, $LOG) || print STDERR "Error opening $LOG: $!\n"; # this dosn't work for some reason?? if (seek(TAIL, $offset, 0)) { # found offset, log not rotated } else { # log reset, follow $offset=0; seek(TAIL, $offset, 0); } } else { open (TAIL, $LOG) || print STDERR "Error opening $LOG: $!\n"; } while ($line = ) { if ($line =~ m/$REASONS/) { $ip = $1; $reason = $2; blockIp($ip, $reason); } } $offset=tell(TAIL); close(TAIL); } } sub removeBlocks { my($line, $ip); print STDERR "Removing old blocks\n"; open(TABLE, "$IPTABLES -L -n|") || die $!; while ($line = ) { if ($line =~ m/^DROP\s*tcp\s*\-\-\s*([\d\.]+)\s/) { $ip = $1; if ($stats{$ip}->{'blocked'} == 1) { print "Duplicate block on $ip, unblock once now\n"; unblockIp($1); } else { print "Unblocking $ip\n"; unblockIp($ip); } } } close(TABLE); } sub unblockIp { my($ip) = @_; iptables('-D', $ip); print "unblock $ip\n"; $stats{$ip}->{'blocked'} = 0; } sub blockIp { my($ip, $reason) = @_; if (($ip eq $MYIP) || ($ip eq '127.0.0.1')) { print STDERR "Sanity check, don't block $ip\n"; return; } if ($stats{$ip}->{'blocked'}) { return; } print "Because of $reason, "; $stats{$ip}->{'blockdte'} = time(); $stats{$ip}->{'blocked'} = 1; iptables('-I', $ip); # print "block $ip\n"; } sub iptables { my($action, $ip) = @_; my(@args) = ($IPTABLES, $action, 'INPUT', '--protocol', 'tcp', '--source', $ip, '--syn', '-j', 'DROP'); while (system(@args)) { print STDERR "@args failed, try again.\n"; sleep 1; } print "@args\n"; } sub getip { my($name) = @_; my($h); unless ($h = gethost($name)) { print "Cannot get ip for $name\n"; return ''; } if (@{$h->addr_list} > 1) { print "Multiple ips for $name\n"; return ''; } return inet_ntoa($h->addr); }