pavement

Firewall, Monitoring

From FreeBSDwiki
(Difference between revisions)
Jump to: navigation, search
(Blanked the page)
Line 1: Line 1:
I wrote myself a handy little CGI application in Perl to let me monitor my [[ipfw]] firewall from a web browser.  It uses (optional) reverse DNS host lookups for the source IPs of the things you're logging, (optional) service lookups from [[ /etc/services]] for the destination port numbers, and (optional) service override lookups for things that you want to look different in the firewall than in /etc/services.  (I personally like to put attack types and such in the overrides file, WITHOUT necessarily winding up obliterating legitimate services that may also use that particular port in my /etc/services file.)
 
  
You can specify alternate logfiles for it to read from the HTTP address, in the format '''<nowiki>http://youraddress/ipfwparser.cgi?logfile=/var/log/security.0.gz</nowiki>''' here, if you like.  Don't sweat GZIPped or BZIP2ed logs; as long as you make sure that the locations of [[gzcat]] and [[bzcat]] specified in the config section are correct (and that you are using .gz and .bz2 extensions on any compressed logfiles), it'll handle the compressed logs transparently.
 
 
One common "gotcha" to remember: if you want this to work from a web browser, you'll need to make sure that your firewall log is readable from the user context of your webserver (in most cases, the user 'www').  Usually you'll want to do this by [[chmod]]ding /var/log/security to 644 - and don't forget to change the value in [[/etc/newsyslog.conf]] as well, or it'll be overwritten the first time your logs rotate!
 
 
<nowiki>#! /usr/bin/perl
 
 
##
 
## ipfwparser.cgi - (c) 2004 Jim Salter
 
## Version 1.0, 2004 Nov 13
 
##
 
## this script is open source software.  you may use it under the terms
 
## of the GNU GPL - you may use it, alter it, redistribute it, and
 
## redistribute any alterations you make as well.  Any changes you make
 
## must also be made available to the public under the same terms.  No
 
## warranties express or implied are made, and you use this software at
 
## your own risk.
 
##
 
## See http://www.opensource.org/licenses/gpl-license.php for a complete
 
## copy of the GNU GPL.
 
##
 
 
#########
 
#########  Config Section
 
#########
 
 
# gzcat utility is used to read GZIP compressed logfiles
 
$gzcat = "/usr/bin/gzcat";
 
 
# bzcat utility is used to read BZIP2 compressed logfiles
 
$bzcat = "/usr/bin/bzcat";
 
 
# location of default logfile - may be overridden with HTTP GET or POST arguments
 
$in{'logfile'} = "/var/log/security";
 
 
# dig command and options are used for reverse DNS lookup
 
$dig_cmd = "/usr/bin/dig -x";
 
$dig_opts = "+short +time=1 +tries=1";
 
$host_lookups = 1;
 
 
# use service lookups from /etc/services and optional overrides from elsewhere
 
$service_lookups = 1;
 
$service_overrides = '/usr/local/etc/service_overrides';
 
 
# get HTTP GET and POST arguments
 
&ReadParse;
 
 
# munge $in{'logfile'} if we see extensions indicating compressed logs
 
if ($in{'logfile'} =~ m/\.gz$/) {$in{'logfile'} = "$gzcat $in{'logfile'} |";}
 
if ($in{'logfile'} =~ m/\.bz2$/) {$in{'logfile'} = "$bzcat $in{'logfile'} |";}
 
 
 
#########
 
#########
 
#########
 
 
 
#### read logfile into an array
 
 
my $counter = 0;                # for use iterating through the array
 
open FH, "$in{'logfile'}";
 
 
foreach (&lt;FH>) {
 
        chomp();
 
        @templine = split (/\s+/, $_);
 
 
        # datestamp
 
        $log[$counter][1] = $templine[0] . '&nbsp;' . $templine[1] . '&nbsp;' . $templine[2];
 
 
        # hostname
 
        $log[$counter][2] = $templine[3];
 
 
        # check for "last message repeated"
 
        if ($templine[4] ne "last") {
 
                # count of specific action
 
                        $log[$counter][0] = 1;
 
                # rule number
 
                        $log[$counter][3] = $templine[6];
 
                # action
 
                        $log[$counter][4] = $templine[7];
 
                # protocol
 
                        $log[$counter][5] = $templine[8];
 
                # source address and port
 
                        my @source = split (/:/, $templine[9]);
 
                        $log[$counter][6] = $source[0];
 
                        $log[$counter][7] = $source[1];
 
                # destination address and port
 
                        my @destination = split (/:/, $templine[10]);
 
                        $log[$counter][8] = $destination[0];
 
                        $log[$counter][9] = $destination[1];
 
 
                # direction
 
                        $log[$counter][10] = $templine[11];
 
                # interface
 
                        $log[$counter][11] = $templine[13];
 
                # check for ICMP in [5] and split out protocol as ports if so
 
                        my @ICMP = split (/:/,$log[$counter][5]);
 
                        if ($ICMP[0] eq 'ICMP') {
 
                                $log[$counter][5] = 'ICMP';
 
                                $log[$counter][7] = $ICMP[1];
 
                                $log[$counter][9] = $ICMP[1];
 
                        }
 
        } else {
 
                # repeat message, parse accordingly
 
                # first, clone the last entry
 
                        for ($loop=0; $loop&lt;12; $loop++) {
 
                                $log[$counter][$loop] = $log[($counter-1)][$loop];
 
                        }
 
                # then replace the count and timestamp portions with current
 
                        $log[$counter][0] = $templine[7];
 
                        $log[$counter][1] = "$templine[0] $templine[1] $templine[2]";
 
        }
 
        $counter ++;
 
}
 
close FH;
 
 
###### Now let's print the results
 
 
&printresults;
 
 
###### Okay, we're done
 
exit;
 
 
##############################################################################
 
#######################                                                    ##
 
####################### Subroutines follow                                  ##
 
#######################                                                    ##
 
##############################################################################
 
 
sub printresults {
 
 
        # set HTML formatting variables
 
        my $headercellbegin = '&lt;td align="center">&lt;font color="#FFFFFF">&lt;b>';
 
        my $headercellend = "&lt;/b>&lt;/font>&lt;/td>\n";
 
        my $bodycellbegin = '&lt;td>&lt;font face="Arial, Helvetica" size="1">';
 
        my $bodycellend = "&lt;/font>&lt;/td>\n";
 
 
 
        # open page for printing as HTML
 
        print "Content-type: text/html\n\n";
 
        print "&lt;html>\n";
 
        print "&lt;head>&lt;/head>\n";
 
        print "&lt;body>\n";
 
 
 
        # if spec'ed, read /etc/services into a hash for fast lookups
 
        if ($service_lookups) {
 
                open FH, "/etc/services";
 
                foreach (&lt;FH>) {
 
                        my @servtemp = split (/\s+/, $_);
 
                        my @portproto = split ('/', $servtemp[1]);
 
                        if ($portproto[1] eq 'tcp') {$portproto[1] = 'TCP';}
 
                        if ($portproto[1] eq 'udp') {$portproto[1] = 'UDP';}
 
                        # assign text to hash index {port}{protocol}
 
                        $service{$portproto[0]}{$portproto[1]} = $servtemp[0];
 
                }
 
                close FH;
 
 
                # if spec'ed, patch results from /etc/services with local overrides
 
                if ($service_overrides) {
 
                        open FH, "$service_overrides";
 
                        foreach (&lt;FH>) {
 
                                my @servtemp = split (/\s+/, $_);
 
                                my @portproto = split ('/', $servtemp[1]);
 
                                if ($portproto[1] eq 'tcp') {$portproto[1] = 'TCP';}
 
                                if ($portproto[1] eq 'udp') {$portproto[1] = 'UDP';}
 
                                # assign text to hash index {port}{protocol}
 
                                $service{$portproto[0]}{$portproto[1]} = $servtemp[0];
 
                        }
 
                        close FH;
 
                }
 
        }
 
 
        print "&lt;table border=1 cellpadding=5 cellspacing=0>\n";
 
        print "&lt;tr bgcolor='#000000'>\n";
 
        print $headercellbegin . '#' . $headercellend;
 
        print $headercellbegin . 'datestamp' . $headercellend;
 
#      print $headercellbegin . 'fwhost' . $headercellend;
 
        print $headercellbegin . 'rule' . $headercellend;
 
        print $headercellbegin . 'action' . $headercellend;
 
        print $headercellbegin . 'proto' . $headercellend;
 
        print $headercellbegin . 'shost' . $headercellend;
 
          if ($host_lookups) {print $headercellbegin . 'shostname' . $headercellend;}
 
        print $headercellbegin . 'sport' . $headercellend;
 
        print $headercellbegin . 'dhost' . $headercellend;
 
        print $headercellbegin . 'dport' . $headercellend;
 
        print $headercellbegin . 'dir' . $headercellend;
 
        print $headercellbegin . 'iface' . $headercellend;
 
          if ($host_lookups) {print $headercellbegin . 'servicename' . $headercellend;}
 
 
        for ($loop = $counter - 1; $loop > -1; $loop--) {
 
                unless ($log[$loop][6] eq '') {
 
                        print "&lt;tr>\n";
 
 
                        for ($element=0; $element&lt;12; $element++) {
 
                                unless ($element eq 2) {print $bodycellbegin . $log[$loop][$element] . $bodycellend;}
 
                                if ($host_lookups * ($element eq 6)) {
 
                                        my $hostname = `$dig_cmd $log[$loop][6] $dig_opts`;
 
                                        if (($hostname =~ m/\&lt;\&lt;\>\>/) + ($hostname eq '')) {$hostname = '&nbsp;';}
 
                                        print $bodycellbegin . $hostname . $bodycellend;
 
                                }
 
                        }
 
                        if ($service_lookups) {print $bodycellbegin . $service{$log[$loop][9]}{$log[$loop][5]} . '&nbsp;' . $bodycellend;}
 
                        print "&lt;/tr>\n";
 
                }
 
        }
 
        print "&lt;/table>\n";
 
        print "&lt;/body>\n";
 
        print "&lt;/html>\n";
 
}
 
 
 
################################################################
 
################################################################
 
 
sub ReadParse {
 
  local (*in) = @_ if @_;
 
  local ($i, $key, $val);
 
 
  # Read in text
 
  if ($ENV{'REQUEST_METHOD'} eq "GET") {
 
    $in = $ENV{'QUERY_STRING'};
 
  } elsif ($ENV{'REQUEST_METHOD'} eq "POST") {
 
    read(STDIN,$in,$ENV{'CONTENT_LENGTH'});
 
  }
 
 
  @in = split(/[&;]/,$in);
 
 
  foreach $i (0 .. $#in) {
 
    # Convert plus's to spaces
 
    $in[$i] =~ s/\+/ /g;
 
 
    # Split into key and value.
 
    ($key, $val) = split(/=/,$in[$i],2); # splits on the first =.
 
 
    # Convert %XX from hex numbers to alphanumeric
 
    $key =~ s/%(..)/pack("c",hex($1))/ge;
 
    $val =~ s/%(..)/pack("c",hex($1))/ge;
 
 
    # Associate key and value
 
    $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator
 
    $in{$key} .= $val;
 
 
  }
 
 
  return scalar(@in);
 
}</nowiki>
 
 
[[Category: Securing FreeBSD]]
 

Revision as of 14:50, 13 August 2012

Personal tools