# HG changeset patch # User Matti Hamalainen # Date 1250431907 -10800 # Node ID 19dace24ad46828a24ad9fb00ea2f19d9c2b2414 # Parent dc072a56f3436a107e7d474f4fb1c81b1ebbb977 Remove default scanfiles; Clean up update_entry() code; Add "SCANFILE_ONCE" setting feature; Add sanity checking of some configuration values; Move more code from main to appropriate functions to handle reinitialization HUPs better. diff -r dc072a56f343 -r 19dace24ad46 example.conf --- a/example.conf Sun Aug 16 05:03:19 2009 +0300 +++ b/example.conf Sun Aug 16 17:11:47 2009 +0300 @@ -72,6 +72,12 @@ SCANFILE = "/var/log/httpd/error.log" SCANFILE = "/var/log/httpd/access.log" +## SCANFILE_ONCE is like SCANFILE setting, but these files are only +## parsed through once, and are NOT followed for changes in "tail -f" +## style as SCANFILEs are. This is useful if you want to include data +## from logrotated files. (Notice that gzip compressed files are not supported.) +SCANFILE_ONCE = "/var/log/auth.log.1" + ############################################################################# ### Checks / tests diff -r dc072a56f343 -r 19dace24ad46 maltfilter --- a/maltfilter Sun Aug 16 05:03:19 2009 +0300 +++ b/maltfilter Sun Aug 16 17:11:47 2009 +0300 @@ -51,13 +51,6 @@ "PASSWD" => "/etc/passwd", ); -# Default logfiles to monitor (SCANFILES setting of configuration overrides these) -my @scanfiles_def = ( - "/var/log/auth.log", - "/var/log/httpd/error.log", - "/var/log/httpd/access.log" -); - my @noblock_ips_def = ( "127.0.0.1", ); @@ -129,6 +122,7 @@ ############################################################################# my $reportmode = 0; # Full report mode my @scanfiles = (); # Files to scan +my @scanfiles_once = (); # Files to scan only once during startup or HUP (e.g. not continuously followed) my @noblock_ips = (); # IPs not to block my %filehandles = (); # Global hash holding opened scanned log filehandles my $pid_file = ""; # Name of Maltfilter daemon pid file @@ -536,8 +530,10 @@ ### Get current Netfilter INPUT table entries that match ### entry types we manage, e.g. blocklist -sub update_blocklist() +sub update_blocklist($) { + # NOTICE: argument not used now + $ENV{"PATH"} = ""; open(STATUS, $settings{"IPTABLES"}." -v -n -L INPUT |") or mdie("Could not execute ".$settings{"IPTABLES"}."\n"); @@ -628,40 +624,47 @@ } } -### Update one entry of +### Update one entry data +sub update_date($$) +{ + if (!defined($_[0]->{"date1"}) || ($_[1] > 0 && $_[0]->{"date1"} < 0)) { + $_[0]->{"date1"} = $_[1]; + } + if (!defined($_[0]->{"date2"}) || $_[1] > $_[0]->{"date2"}) { + $_[0]->{"date2"} = $_[1]; + } +} + sub update_entry($$$$$$) { my ($struct, $mip, $mdate, $mclass, $mreason, $addhits) = @_; + $struct->{$mip} = {} unless defined($struct->{$mip}); + my $entry = $struct->{$mip}; + $struct->{$mip}{"reason"}{$mclass} = {}; + my $reason = $struct->{$mip}{"reason"}{$mclass}; + + # Add hits only when requested if ($addhits) { - $struct->{$mip}{"hits"}++; - $struct->{$mip}{"reason"}{$mclass}{"hits"}++; + $entry->{"hits"}++; + $reason->{"hits"}++; } else { - $struct->{$mip}{"hits"} = 1 unless defined($struct->{$mip}{"hits"}); - $struct->{$mip}{"reason"}{$mclass}{"hits"} = 1 unless defined($struct->{$mip}{"reason"}{$mclass}{"hits"}); - } - - if ($reportmode) { - push(@{$struct->{$mip}{"reason"}{$mclass}{"msg"}}, $mreason); - } else { - $struct->{$mip}{"reason"}{$mclass}{"msg"} = $mreason; + $entry->{"hits"} = 1 unless defined($entry->{"hits"}); + $reason->{"hits"} = 1 unless defined($reason->{"hits"}); } - if (!defined($struct->{$mip}{"date1"}) || ($mdate > 0 && $struct->{$mip}{"date1"} < 0)) { - $struct->{$mip}{"date1"} = $mdate; - } - if (!defined($struct->{$mip}{"date2"}) || $mdate > $struct->{$mip}{"date2"}) { - $struct->{$mip}{"date2"} = $mdate; - } - - if (!defined($struct->{$mip}{"reason"}{$mclass}{"date2"}) || ($mdate > 0 && $struct->{$mip}{"reason"}{$mclass}{"date2"} < 0)) { - $struct->{$mip}{"reason"}{$mclass}{"date2"} = $mdate; - } - if (!defined($struct->{$mip}{"reason"}{$mclass}{"date2"}) || $mdate > $struct->{$mip}{"reason"}{$mclass}{"date2"}) { - $struct->{$mip}{"reason"}{$mclass}{"date2"} = $mdate; + # Messages is an array in reportmode + if ($reportmode) { + push(@{$reason->{"msg"}}, $mreason); + } else { + $reason->{"msg"} = $mreason; } - return $struct->{$mip}{"hits"}; + # Update timestamps (generic and reason) + update_date($entry, $mdate); + update_date($reason, $mdate); + + return $entry->{"hits"}; } ### Check if given "try count" exceeds treshold and if entry @@ -720,29 +723,39 @@ ### Like Perl's die(), but also print a logfile entry. sub mdie($) { - mlog(-1, $_[0]); + mlog(-1, $_[0]) if ($LOGFILE); die($_[0]); } ### Initialize sub malt_init { - mlog(0, "Updating initial blocklist from netfilter.\n"); - update_blocklist(); + foreach my $filename (@scanfiles_once) { + mlog(0, "Parsing [once] ".$filename." ...\n"); + if (open(INFILE, "<", $filename)) { + while () { + chomp; + check_log_line($_); + } + } else { + mlog(-1, "Could not open '".$filename."', skipping now.\n"); + } + close(INFILE); + } foreach my $filename (@scanfiles) { local *INFILE; mlog(0, "Parsing ".$filename." ...\n"); - open(INFILE, "<", $filename) or mdie("Could not open '".$filename."'!\n"); - $filehandles{$filename} = *INFILE; - while () { - chomp; - check_log_line($_); + if (open(INFILE, "<", $filename)) { + $filehandles{$filename} = *INFILE; + while () { + chomp; + check_log_line($_); + } + } else { + mlog(-1, "Could not open '".$filename."', skipping now.\n"); } } - - mlog(0, "Weeding old entries.\n"); - weed_entries(); } ### Quick cleanup (not complete shutdown) @@ -810,7 +823,7 @@ # (in case entries have appeared there from "outside") # and perform weeding of old entries. $counter = 0; - update_blocklist(); + update_blocklist(time()); weed_entries(); generate_status($settings{"STATUS_FILE_PLAIN"}, 0); generate_status($settings{"STATUS_FILE_HTML"}, 1); @@ -848,7 +861,9 @@ my $key = uc($1); my $value = $2; if ($key eq "SCANFILE") { - push(@scanfiles_def, $value); + push(@scanfiles, $value); + } elsif ($key eq "SCANFILE_ONCE") { + push(@scanfiles_once, $value); } elsif ($key eq "NOBLOCK_IPS") { push(@noblock_ips_def, $value); } elsif (defined($settings{$key})) { @@ -874,13 +889,43 @@ sub malt_configure { # Let user define his/her own logfiles to scan - @scanfiles_def = (); - undef(@scanfiles_def); + @scanfiles = (); + undef(@scanfiles); + + @scanfiles_once = (); + undef(@scanfiles_once); + foreach my $filename (@configfiles) { mdie("Errors in configuration file '$filename', bailing out.\n") unless (malt_read_config($filename) == 0); } + # Clean up certain arrays duplicate entries + my %saw = (); + @scanfiles = grep(!$saw{$_}++, @scanfiles); + + %saw = (); + @scanfiles_once = grep(!$saw{$_}++, @scanfiles_once); + + %saw = (); + @noblock_ips = grep(!$saw{$_}++, @noblock_ips_def); + undef(%saw); + + mlog(-1, "Not blocking following IPs: ".join(", ", @noblock_ips)."\n"); + + # Check if we have anything to do + if ($reportmode) { + mdie("Nothing to do, no SCANFILE(s) or SCANFILE_ONCE(s) defined in configuration.\n") unless ($#scanfiles > 0 || $#scanfiles_once > 0); + } else { + mdie("Nothing to do, no SCANFILE(s) defined in configuration.\n") unless ($#scanfiles > 0); + } + + # Test existence of iptables + if (! -e $settings{"IPTABLES"} || ! -x $settings{"IPTABLES"}) { + mdie("iptables binary does not exist or is not executable: ".$settings{"IPTABLES"}."\n"); + } + + # Check settings mdie("SYSACCT_MIN_UID must be >= 1.\n") unless ($settings{"SYSACCT_MIN_UID"} >= 1); mdie("SYSACCT_MAX_UID must be >= SYSACCT_MIN_UID.\n") unless ($settings{"SYSACCT_MAX_UID"} >= $settings{"SYSACCT_MIN_UID"}); @@ -932,15 +977,6 @@ malt_configure(); - -# Clean up certain arrays duplicate entries -my %saw = (); -@scanfiles = grep(!$saw{$_}++, @scanfiles_def); - -%saw = (); -@noblock_ips = grep(!$saw{$_}++, @noblock_ips_def); -undef(%saw); - # Open logfile if ($settings{"DRY_RUN"}) { print $progbanner. @@ -953,14 +989,9 @@ mlog(-1, "Log started\n"); } -# Test existence of iptables -if (! -e $settings{"IPTABLES"} || ! -x $settings{"IPTABLES"}) { - mdie("iptables binary does not exist or is not executable: ".$settings{"IPTABLES"}."\n"); -} - -mlog(-1, "Not blocking following IPs: ".join(", ", @noblock_ips)."\n"); - # Initialize +mlog(0, "Updating initial blocklist from netfilter.\n"); +update_blocklist(-1); malt_init(); # Fork to background, unless dry-running