Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

denies

by Anonymous Monk
on Jul 28, 2000 at 07:07 UTC ( #24794=sourcecode: print w/ replies, xml ) Need Help??

Category: Utility Scripts
Author/Contact Info Richard Griswold griswold@acm.org
Description: Creates and mails a report on ipchains DENY entries in the /var/log/messages* files. The script remembers the last entry from the last run so you only get new entries on the next run. Add it to your crontab to generate periodic reports.
#!/usr/bin/perl
use strict;

# Usage variables
my $execname;   # Name of script
my $usage;      # Usage info

# Configuration variables
my $badport;    # List of ports to give extra warning about
my $lastdeny;   # Last DENY entry from the last run
my $blankrep;   # If TRUE, send a report even if nothing happened
my $ignoreport; # Comma separated list of ports to ignore
my $mailto;     # Address to mail report to

# Parts of the DENY message
my $destport;   # Destination port in DENY message
my $sourceaddr; # Source address in DENY message

# Variables used for creating report
my $lenaddr;    # Length of an address
my $lencount;   # Length of a port count
my $lenport;    # Length of a port
my $port;       # A single port from %addr hash
my $addr;       # A single address from %addr hash

# Other variables
my $i;          # Index
my $numdenies;  # Total number of deny entries since last run
my $msgfiles;   # All "message" files in /var/log, in reverse order

# Lists and hashes
my @denies;     # List of DENY entries
my %addr;       # All source addresses that have been found
my %badport;    # "Bad" ports, and number of times found
my %ignoreport; # Ports to ignore, and number of times found
my %ports;      # All ports for a given address

# Initialize variables
$numdenies =  0;
$execname  = $0;
$execname  =~ s/^.+\/(.+)$/\1/;
$usage     = "
Usage: $execname

  The \"$execname\" script checks for DENY entries in
  /var/log/messages* and mails a report with any new entries since
  the last run to the address specified in /etc/deniesrc.  This file
  also has an optional list of destination ports to ignore, \"bad\"
  ports to flag in the report (such as ports used by Back Orifice
  and the ilk), and other parameters:

    LASTDENY:   The last DENY entry from the last run.  This is used
                to determine where to start for the next run.  Do
                not edit this unless you know what you are doing.
    BLANKREP:   If this is \"True\", mail a report even if there are
                no new DENY entries.  If this is \"False\", only
                mail a report if there are new DENY entries.
    IGNOREPORT: List of ports to ignore.  The format is
                \"68, 113, 137\".
    BADPORT:    List of ports to give special attention to.  The
                format is \"27374, 31337\".
    MAILTO:     Address to mail the report to.  For example
                \"root\".

  Richard Griswold, griswold\@acm.org  -  25 JUL 2000
";

# Get settings
( $lastdeny, $blankrep, $ignoreport, $badport, $mailto ) =
  &getSettings();

# Get list of DENY messages
$msgfiles = join ' ', sort { $b cmp $a } split /\n/,
            `ls /var/log/messages*`;
@denies   = split /\n/, `grep -h DENY $msgfiles`;

# Check if any denies
if ( $#denies == -1 ) {
  if ( $blankrep eq "TRUE" ) {
    &mailBlank( $mailto, $ignoreport, $badport, $lastdeny );
  }
  exit;
}

# Build ignore and bad port hashes
%badport    = &createHash( $badport    );
%ignoreport = &createHash( $ignoreport );

# Not run previously if $lastdeny is zero
if ( $lastdeny eq "NONE" ) {
  $i = 0;
} else {
  # Find $lastdeny in @denies
  for ( $i = 0; $i <= $#denies; $i++ ) {
    if ( $denies[$i] eq $lastdeny ) {
      last;
    }
  }

  # Adjust index
  if ( $i < $#denies ) {       # Go to next entry, if there is one
    $i++;
  } elsif ( $i == $#denies ) { # No new entries if at end of list
    if ( $blankrep eq "TRUE" ) {
      &mailBlank( $mailto, $ignoreport, $badport, $lastdeny );
    }
    exit;
  } else {               # Past end of list, so all entries are new
    $i = 0;
  }
}

# Run through all entries since $lastdeny
for ( ; $i <= $#denies; $i++, $numdenies++ ) {
  # Get destination port for this DENY entry
  $destport =  $denies[$i];
  $destport =~ s/^.+:[0-9]+ [0-9\.]+:([0-9]+) .+$/$1/;

  # If $destport is one to ignore, increment count
  # and skip to next port
  if ( $ignoreport{$destport} ne '' ) {
    $ignoreport{$destport}++;
    next;
  }

  # If $destport is a bad port, increment count
  if ( $badport{$destport} ne '' ) {
    $badport{$destport}++;
  }

  # Get source address
  $sourceaddr =  $denies[$i];
  $sourceaddr =~ s/^.+ ([0-9\.]+):[0-9]+ [0-9\.]+.+$/$1/;

  # Add port to list for this address
  $addr{$sourceaddr}{$destport}++;  # A hash of hashes :)
}

# Write last entry to /etc/deniesrc
&writeEntry( $denies[$#denies] );

# ---------- Print report ----------

# Heading
open PROC, "| mail -s\"Denies report for ".localtime()."\" $mailto";

# Total number of DENY entries
print PROC
  "There were $numdenies new entries since the last run\n\n";

# Ignored ports
$lenport = 4; $lencount = 5;
foreach ( keys %ignoreport ) {
  $i = length $_;              if ( $i>$lenport  ) { $lenport =$i; }
  $i = length $ignoreport{$_}; if ( $i>$lencount ) { $lencount=$i; }
}
print PROC "Ignored ports:" . ' ' x ( $lenport  - 3 ) .
           "Port"           . ' ' x ( $lencount - 2 ) . "Times\n";
foreach ( sort {$a <=> $b} ( keys %ignoreport ) ) {
  print PROC
    ' ' x ( 15 + $lenport  - length $_ )         . $_ .
    ' ' x (  3 + $lencount - length $ignoreport{$_} ) .
    "$ignoreport{$_}\n";
}
print PROC "\n";

# "Bad" ports
$lenport = 4; $lencount = 5;
foreach ( keys %badport ) {
  $i = length $_;           if ( $i>$lenport  ) { $lenport  = $i; }
  $i = length $badport{$_}; if ( $i>$lencount ) { $lencount = $i; }
}
print PROC "Bad ports:" . ' ' x ( $lenport  + 1 ) .
           "Port"       . ' ' x ( $lencount - 2 ) . "Times\n";
foreach ( sort {$a <=> $b} ( keys %badport ) ) {
  print PROC ' ' x ( 15 + $lenport  - length $_ ) .
        $_ . ' ' x (  3 + $lencount - length $badport{$_} ) .
        "$badport{$_}\n";
}
print PROC "\n";

# Non-ignored addresses and ports
$lenaddr = 7; $lenport = 4; $lencount = 5;
foreach ( keys %addr ) {
  $i = length $_; if ( $i > $lenaddr  ) { $lenaddr = $i; }
  foreach $port ( keys %{ $addr{$_} } ) {
    $i = length $port;            if ( $i>$lenport  ){$lenport =$i;}
    $i = length $addr{$_}{$port}; if ( $i>$lencount ){$lencount=$i;}
  }
}
print PROC "Address" . ' ' x ( $lenaddr + $lenport - 8 ) .
           "Port"    . ' ' x ( $lencount - 2 ) . "Times\n";
foreach ( sort sortAddr ( keys %addr ) ) {
  $addr = $_;
  foreach $port ( sort {$a <=> $b} ( keys %{ $addr{$_} } ) ) {
    print PROC $addr .
      ' ' x ( 3 + $lenaddr + $lenport -
        length( $addr ) - length $port ) .
      $port . ' ' x ( 3 + $lencount - length $addr{$_}{$port} ) .
      "$addr{$_}{$port}\n";
    # Blank out address for subsequent ports
    $addr = ' ' x length $addr;
  }
  print PROC "\n";
}

# Settings
print PROC "\nSettings:\n";
print PROC "  Ignored ports:      $ignoreport\n";
print PROC "  Bad ports:          $badport\n";
print PROC "  Mail blank reports: $blankrep\n";
print PROC "  Mail report to:     $mailto\n";
print PROC "  Last DENY entry:    $lastdeny\n\n";

# Raw DENY entries
print PROC "\nRaw DENY entries:\n";
for ( $i = $#denies - $numdenies + 1; $i <= $#denies; $i++ ) {
  print PROC "  $denies[$i]\n";
}

print PROC "--- End of report ---\n";

#--------------
# getSettings |
#-------------------------------------------------------------------
# Get settings from /etc/deniesrc file
#-------------------------------------------------------------------
sub getSettings {
  my $ignoreport;  # List of ports to ignore
  my $badport;     # List of ports to give extra warning about
  my $lastdeny;    # Last DENY entry from the last run
  my $blankrep;    # If TRUE, send a report even if nothing happened
  my $mailto;      # Address to mail report to

  # Defaults
  $blankrep = "TRUE";
  $ignoreport = $badport = $lastdeny = $mailto = "NONE";

  # Create RC file if necessary
  if ( not -e "/etc/deniesrc" ) {
    &createRCFile();
    $mailto = "root";
    return $lastdeny, $blankrep, $ignoreport, $badport, $mailto;
  }

  # Check if we can read and write RC file
  -r "/etc/deniesrc" or die "Cannot read /etc/deniesrc\n";
  -w "/etc/deniesrc" or die "Cannot write /etc/deniesrc\n";

  # Open RC file
  open( RCFILE, "</etc/deniesrc" ) or
    die "Cannot open resource file /etc/deniesrc for reading\n";

  # Get options
  foreach ( <RCFILE> ) {
    chop;
    if ( ( $_ =~ /^ *#.+$/ ) or ( $_ eq "" ) ) {
      next;
    } elsif ( $_ =~ /^LASTDENY:/ ) {
      $lastdeny =  $_;
      $lastdeny =~ s/^LASTDENY: +(.+)$/$1/;
    } elsif ( $_ =~ /^BLANKREP:/ ) {
      $blankrep =  $_;
      $blankrep =~ s/^BLANKREP: +(.+)$/$1/;
    } elsif ( $_ =~ /^IGNOREPORT:/ ) {
      $ignoreport =  $_;
      $ignoreport =~ s/^IGNOREPORT: +(.+[0-9]+) *$/$1/;
    } elsif ( $_ =~ /^BADPORT:/ ) {
      $badport =  $_;
      $badport =~ s/^BADPORT: +(.+[0-9]+) *$/$1/;
    } elsif ( $_ =~ /^MAILTO:/ ) {
      $mailto =  $_;
      $mailto =~ s/^MAILTO: +(.+)$/$1/;
    } else {
      print
        "Line \"$_\" is not valid in /etc/deniesrc.  Ignoring.\n";
    }
  }

  close RCFILE;

  # Validate options
  if ( $blankrep ne "NONE" ) {
    if ( ( uc( $blankrep ) ne "TRUE"  ) and
         ( uc( $blankrep ) ne "FALSE" ) ) {
      print
   "Keyword \"$blankrep\" is not valid for option \"BLANKREP:\".\n";
      print "  Ignoring.\n";
    }
    $blankrep = uc $blankrep;
  }

  if ( $ignoreport ne "NONE" ) {
    if ( &validPortList( $ignoreport ) < 0 ) {
      print
  "List \"$ignoreport\" is not valid for option \"IGNOREPORT:\".\n";
      print "  Ignoring.\n";
      $ignoreport = "";
    }
  }

  if ( $badport ne "NONE" ) {
    if ( &validPortList( $badport ) < 0 ) {
      print
        "List \"$badport\" is not valid for option \"BADPORT:\".\n";
      print "  Ignoring.\n";
      $badport = "";
    }
  }

  if ( $mailto eq "NONE" ) {
    $mailto = "root";
  }

  return $lastdeny, $blankrep, $ignoreport, $badport, $mailto;
}

#---------------
# createRCFile |
#-------------------------------------------------------------------
# Create the /etc/deniesrc file
#-------------------------------------------------------------------
sub createRCFile {
  open FILE ,">/etc/deniesrc" or
    die "Cannot create /etc/deniesrc file\n";

  print FILE
   "# RC file for \"denies\" script\n\n";
  print FILE
   "# NOTE:  Do not edit the following line:\n";
  print FILE
   "LASTDENY:\n\n";
  print FILE
   "# Set this line to \"False\" if you do not want to get blank\n";
  print FILE
   "# reports (reports when there are no DENY entries).\n";
  print FILE
   "BLANKREP:   True\n\n";
  print FILE
   "# Put the list of ports to ignore on this line.\n";
  print FILE
   "# Use the format \"68, 113, 137\".\n";
  print FILE
   "IGNOREPORT: NONE\n\n";
  print FILE
   "# Put the list of \"bad\" ports on this line.  These are \n";
  print FILE
   "# ports you want extra notification about, such as the \n";
  print FILE
   "# Back Orifice 31337 port.  Use the same format as above.\n";
  print FILE
   "BADPORT:    NONE\n\n";
  print FILE
   "# Put the address you want to send the report to here.\n";
  print FILE
   "MAILTO:     root\n";

  close FILE;
  chmod 0600, "/etc/deniesrc";
}

#----------------
# validPortList |
#-------------------------------------------------------------------
# Check if a port list is in a valid format (ie 10, 31, 138, ...)
#-------------------------------------------------------------------
sub validPortList {
  foreach ( split /, /, shift ) {
    if ( $_ !~ /^[0-9]+$/ ) {
      return -1;
    }
  }

  return 0;
}

#------------
# mailBlank |
#-------------------------------------------------------------------
# Mail a blank report
#-------------------------------------------------------------------
sub mailBlank {
  my $mailto     = shift;
  my $ignoreport = shift;
  my $badport    = shift;
  my $lastdeny   = shift;

  open PROC,
    "| mail -s\"Denies report for ".localtime()."\" $mailto";
  print PROC "There were no new entries for since the last run\n\n";
  print PROC "Settings:\n";
  print PROC "  Ignored ports:      $ignoreport\n";
  print PROC "  Bad ports:          $badport\n";
  print PROC "  Mail blank reports: $blankrep\n";
  print PROC "  Mail report to:     $mailto\n";
  print PROC "  Last DENY entry:    $lastdeny\n\n";
  print PROC "--- End of report ---\n";
  close PROC;
}

#-------------
# createHash |
#-------------------------------------------------------------------
# Create a hash from a comma separated list
#-------------------------------------------------------------------
sub createHash {
  my $list = shift;

  $list =~ s/, /, 0, /g;
  $list .= ", 0";

  return split /, /, $list;
}

#-------------
# writeEntry |
#-------------------------------------------------------------------
# Write last DENY entry inot /etc/deniesrc
#-------------------------------------------------------------------
sub writeEntry {
  my $lastdeny = shift;
  my @rcfile;

  open FILE, "</etc/deniesrc" or
    die "Cannot open resource file /etc/deniesrc for reading\n";

  foreach ( <FILE> ) {
    if ( $_ =~ /^LASTDENY:/ ) {
      $_ =~ s/^(LASTDENY:)$/$1   /;        # Put in spaces if none
      $_ =~ s/^(LASTDENY: +).*$/$1$lastdeny/;
    }
    push @rcfile, $_;
  }
  close FILE;

  open FILE ,">/etc/deniesrc" or
    die "Cannot open resource file /etc/deniesrc for writing\n";
  foreach ( @rcfile ) {
    print FILE $_;
  }
  close FILE;
}

#-----------
# sortAddr |
#-------------------------------------------------------------------
# Sort IP addresses
#-------------------------------------------------------------------
sub sortAddr {
  my @a = split /\./, $a;
  my @b = split /\./, $b;
  my $i;

  for ( $i = 0; $i < 4; $i++ ) {
    if ( $a[$i] < $b[$i] ) {
      return -1;
    } elsif ( $a[$i] > $b[$i] ) {
      return 1;
    }
  }
  return 0;
}

Comment on denies
Download Code

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://24794]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (13)
As of 2014-07-25 18:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (174 votes), past polls