at2marty has asked for the wisdom of the Perl Monks concerning the following question:

I wrote a small script to parse my log files, retrieve ip addresses, add them to a blacklist and append rules to my firewall (iptables).

My first question is, in the first "while loop" I used some regex's to get the data that I wanted. Is there a better way to perhaps combine them on fewer lines?

My second question is, the second "while loop" opens the file that I just appended to, and filters out blank lines, IP addresses on my network and duplicate entries. Is there a way that I can do that possibly within the first "while loop"?

I guess what I'm trying to accomplish with my second question is cut down on the number of times I open and close the same file.

I am still learning and am open to any suggestions. Here is what I have now.

#!/usr/bin/perl -w # # This script grabs ip addresses from my firewall log file # and adds them to a blacklist for my iptables ruleset. # ## NOTE - This script must be run as root use strict; # Check to make sure root is running this if ($< != 0) { print "You must run this program as root!\n"; exit } my $log = "/var/log/iptables.log"; my $blacklist = "/var/log/blacklist"; my @list; my %seen; my @sorted; # Open log file, retrieve list of ip addresses and write them # to the blacklist open(IN, "<", $log) || die "Can not open $log $!"; open(BL, ">>", $blacklist) || die "Can not open $blacklist $!"; while (<IN>) { s/.*(SRC)/$1/; s/(DST).*/$1/; s/ DST//; s/SRC=//; print BL ; } close IN; close BL; # Read blacklist into an array while eliminating blank lines, # IP's from my network and duplicates open(BL, "<", $blacklist) || die "can not open $blacklist $!"; while (<BL>) { next if /\A\s*\z/ ; # skip blank lines next if /192.168*/; $seen{$_}++; next if $seen{$_} > 1; push(@list, $_); } close BL; # Sort my list of IP addresses @sorted = sort { pack('C4' => $a =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) cmp pack('C4' => $b =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) } @list; # Create clean blacklist file and append iptables rules open(BL, ">", $blacklist) || die "Cannot open $blacklist $!"; foreach my $ip(@sorted) { print BL "$ip"; chomp($ip); system("/sbin/iptables -A BLACKLIST -p all -s $ip -d 0/0 -j LOG -- +log-prefix \"IPTABLES:Blacklist: \""); system("/sbin/iptables -A BLACKLIST -p all -s $ip -d 0/0 -j DROP") +; } close BL; chmod 0600, "$blacklist";

Replies are listed 'Best First'.
Re: Questions regarding regular expressions and arrays
by jwkrahn (Abbot) on Dec 12, 2011 at 23:19 UTC

    What does your data look like?

      The typical log file entry looks like this (all on one line). Note, the x's replace the real mac and source ip just for anonymity.

      Dec 10 13:14:39 mymachine kernel: [74697.914492] IPTABLES:Blacklist: IN=eth0 OUT= MAC=xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx SRC=xxx.xxx.xxx.xxx DST=192.168.1.10 LEN=60 TOS=0x00 PREC=0x00 TTL=38 ID=25551 DF PROTO=TCP SPT=53521 DPT=22 WINDOW=14600 RES=0x00 SYN URGP=0

        You could probably use something like this:

        #!/usr/bin/perl use warnings; use strict; # # This script grabs ip addresses from my firewall log file # and adds them to a blacklist for my iptables ruleset. # ## NOTE - This script must be run as root use Socket; # Check to make sure root is running this $< and die "You must run this program as root!\n"; my $log = '/var/log/iptables.log'; my $blacklist = '/var/log/blacklist'; # Open log file, retrieve list of ip addresses and write them # to the blacklist open IN, "<", $log or die "Can not open $log $!"; my %seen; while ( <IN> ) { next unless /\S/; if ( /SRC=([0-9.]+) / ) { next if $1 =~ /^192\.168/; $seen{ inet_aton( $1 ) }++; } } close IN; # Sort my list of IP addresses my @sorted = map inet_ntoa( $_ ), sort keys %seen; # Create clean blacklist file and append iptables rules open BL, '>', $blacklist or die "Cannot open $blacklist $!"; foreach my $ip ( @sorted ) { print BL "$ip\n"; 0 == system '/sbin/iptables', '-A', 'BLACKLIST', '-p', 'all', '-s' +, $ip, '-d', '0/0', '-j', 'LOG', '--log-prefix', 'IPTABLES:Blacklist: + ' or die "system /sbin/iptables failed: $?"; 0 == system '/sbin/iptables', '-A', 'BLACKLIST', '-p', 'all', '-s' +, $ip, '-d', '0/0', '-j', 'DROP' or die "system /sbin/iptables failed: $?"; } close BL; chmod 0600, $blacklist;