#!/usr/bin/perl
#< mailmon.pl - Gather Postfix statistics from the current /var/log/maillog file
# on a simple SMTP relay.
# This script will gather statistics showing the top 10 authorised hosts relaying
# through the SMTP relay on a Postfix host. It will also show a count of relay attempts
# made from hosts that aren't authorised to use the relay.
#
# Change the $log_source and $log_dest variables as appropriate.
# Will also check against /etc/postfix/mynetworks to ensure that any host being denied
# access hasn't recently been added to the ACL. If it has, it'll ignore it.
use strict;
use warnings;
use File::Copy;
our @lines;
our $hostname;
&check_uid;
©_maillog;
&get_hostname;
&process_maillog;
sub check_uid() {
my $effective_uid = $<;
if ( $effective_uid ne 0 ) {
printf( "<- You must be root to run this script\n" );
exit 1;
}
}
sub get_hostname() {
open( HOSTNAME, "/bin/hostname |" );
$hostname = <HOSTNAME>;
chomp( $hostname );
close( HOSTNAME );
}
sub copy_maillog() {
# change the following two variables to suit your environment
my $log_source = "/var/log/maillog";
my $log_dest = "/home/mailmon/maillog";
printf( "-> Copying %s to %s\n", $log_source, $log_dest );
copy( $log_source, $log_dest ) || die ( "<- Cannot copy maillog\n" );
printf( "-> Copy operation successful\n" );
open( LOG, "<$log_dest" ) || die ( "<- Cannot open maillog for reading\n" );
@lines = <LOG>;
close( LOG );
}
sub process_maillog() {
my $first_line;
my $last_line;
my $first_line_timestamp;
my $last_line_timestamp;
my $denied_line;
my $count;
my $denied_count;
my @denied_array;
my $accepted_count;
my @accepted_array;
my %clients;
$count = 0;
foreach ( @lines ) {
$count++;
$first_line = $_ if ( $count == 1 );
# relay access denied
if ( $_ =~ /Relay access denied/ ) {
$denied_line = $_;
my $denied_ip;
#my $denied_hostname;
#$denied_line =~ s/^.*from (.*)\[([0-9.]*)\].*$/$2 \( $1 \)/;
$denied_ip = $denied_line;
#$denied_hostname = $denied_line;
$denied_ip =~ s/^.*from .*\[([0-9.]*)\].*$/$1/;
#$denied_hostname =~ s/^.*from (.*)\[[0-9.]*\].*$/$1/;
chomp( $denied_ip );
#chomp( $denied_hostname );
#$denied_array[ $denied_count++ ] = sprintf( "%-16s( %s )", $denied_ip, $denied_hostname);
$denied_array[ $denied_count++ ] = $denied_ip;
}
if ( $_ =~ /client/ ) {
my $client_line;
my $client_message_id;
my $client_ip;
#my $client_hostname;
my $client_id;
$client_line = $_;
$client_message_id = $client_line;
$client_ip = $client_line;
#$client_hostname = $client_line;
$client_message_id =~ s/^.*smtpd\[[0-9]+\]: ([^:]+):.*$/$1/;
$client_ip =~ s/^.*client\=[^\[]+\[([0-9.]+)\].*$/$1/;
#$client_hostname =~ s/^.*client\=([^\[]+).*$/$1/;
chomp( $client_message_id );
chomp( $client_ip );
#chomp( $client_hostname );
#$client_id = sprintf( "%-16s( %s )", $client_ip, $client_hostname);
$client_id = $client_ip;
$clients{$client_message_id} = $client_id;
}
if ( $_ =~ /accepted/ ) {
my $accepted_line;
$accepted_line = $_;
my $accepted_message_id;
$accepted_message_id = $accepted_line;
$accepted_message_id =~ s/^.*smtp\[[0-9]+\]: ([^:]+):.*$/$1/;
chomp( $accepted_message_id );
if ( $clients{$accepted_message_id} ) {
$accepted_array[ $accepted_count++ ] = $clients{$accepted_message_id};
}
}
}
$last_line = $lines[-1];
# print header information
$first_line_timestamp = $first_line;
$last_line_timestamp = $last_line;
$first_line_timestamp =~ s/^(.*)$hostname postfix.*$/$1/;
$last_line_timestamp =~ s/^(.*)$hostname postfix.*$/$1/;
chomp( $first_line_timestamp );
chomp( $last_line_timestamp );
printf( "\n\n-> %s maillog report\n", $hostname );
printf( "-> Data range from %s to %s\n", $first_line_timestamp, $last_line_timestamp );
printf( "\n-> Hosts denied access to this relay\n\n" );
&sort_denied_array ( @denied_array );
printf( "\n-> Top 10 authorised hosts using this relay\n\n" );
&sort_accepted_array ( @accepted_array );
printf( "\n" );
}
sub sort_accepted_array() {
my @array = @_;
my %count;
my $good_ip;
my $good_count;
my $key;
my $limiter;
map { $count{$_}++ } @array;
#printf( "%-10s %s\n%-10s %s\n", "Count", "Host", '='x10, '='x60 );
printf( "%-10s %s\n%-10s %s\n", "Count", "Host", '='x10, '='x20 );
foreach $key ( sort { $count{$b} <=> $count{$a} } keys %count ) {
$good_ip = $key;
$good_count = $count{$key};
chomp( $good_ip );
chomp( $good_count );
printf( "%-10d %s\n", $good_count, $good_ip );
$limiter++;
return if ( $limiter == 10 );
}
}
sub sort_denied_array() {
my @array = @_;
my %count;
my $bad_ip;
my $bad_count;
my $key;
open( MYNETWORKS, "< /etc/postfix/mynetworks" );
my @mynetworks;
my $flag;
@mynetworks = <MYNETWORKS>;
close(MYNETWORKS);
map { $count{$_}++ } @array;
#printf( "%-10s %s\n%-10s %s\n", "Count", "Host", '='x10, '='x60 );
printf( "%-10s %s\n%-10s %s\n", "Count", "Host", '='x10, '='x20 );
foreach $key ( sort { $count{$b} <=> $count{$a} } keys %count ) {
$flag = 0;
$bad_ip = $key;
$bad_count = $count{$key};
chomp( $bad_ip );
chomp( $bad_count );
# this check is to ensure that the host hasn't been added to the ACL
foreach ( @mynetworks ) {
my $mynetworksline;
$mynetworksline = $_;
if ( $mynetworksline =~ /^[ ]*#/ ) {
# do nothing
} else {
$mynetworksline =~ s/^[ ]?([0-9.]+)$/$1/;
chomp( $mynetworksline );
if ( $bad_ip eq $mynetworksline ) {
$flag = 1;
}
}
}
if ( $flag eq 0 ) {
printf( "%-10d %s\n", $bad_count, $bad_ip );
}
}
}
exit 0;