Having a wish to do what the OP wants and being endowed with the super-power of reading :-), I followed the pointers to Email::Stuffer and wrote myself the script below to parse nginx logs & email me every day about who has been trying to access my server (only a few of us should do that). I have given the whole script in case you or anyone wants to trace everything, but the relevant parts are:
package Nginxlog;
use strict;
use warnings;
use feature 'say';
use Data::Dumper; #for debugging
use Email::Sender::Transport::SMTP;
use Email::Stuffer;
use Email::Valid;
use Getopt::Long;
use Net::Whois::IP;
use Pod::Usage;
use Regexp::Common qw (net);
use Scalar::Util qw(openhandle);
my ($opt_debug, $opt_help, $opt_man, $opt_versions,
$opt_ignore, $opt_log, $opt_server, $opt_email);
GetOptions(
'debug!' => \$opt_debug,
'help!' => \$opt_help,
'man!' => \$opt_man,
'versions!' => \$opt_versions,
'ignore:s' => \$opt_ignore,
'log=s' => \$opt_log,
'email=s' => \$opt_email,
'server=s' => \$opt_server,
) or pod2usage(-verbose => 1) && exit;
pod2usage(-verbose => 1) && exit if defined $opt_help;
pod2usage(-verbose => 2) && exit if defined $opt_man;
END{
if(defined $opt_versions){
print
"\nModules, Perl, OS, Program info:\n",
" Data::Dumper $Data::Dumper::VERSION\n",
" Email::Stuffer $Email::Stuffer::VERSION\n",
" Email::Sender::Transport::SMTP $Email::Sender::Transport::SMTP::VER
+SION\n",
" Getopt::Long $Getopt::Long::VERSION\n",
" Net::Whois::IP $Net::Whois::IP::VERSION\n",
" Pod::Usage $Pod::Usage::VERSION\n",
" Regexp::Common $Regexp::Common::VERSION\n",
" Scalar::Util $Scalar::Util::VERSION\n",
" strict $strict::VERSION\n",
" Perl $]\n",
" $0 0.0.1\n",
"\n\n";
}
}
main() unless caller();
my $report;
my $ignore;
sub main {
my $log;
$log = setlog($opt_log);
$opt_ignore = setignore($opt_ignore);
checkemail($opt_email);
checkserver($opt_server);
open my $fhlog, '<', $opt_log or die "Error opening log file: $!";
open my $fhignore, '<', $opt_ignore; # May be nothing to ignore
if (openhandle($fhignore)) {
say 'Reading ignore list from file' if $opt_debug;
$ignore = do {local $/; <$fhignore>;};
close $fhignore;
}
$ignore //= '';
say "Ignore list is ->$ignore<-." if $opt_debug;
parselog($fhlog);
say $report if $opt_debug;
sendreport() unless ($opt_debug);
}
sub setlog {
my $log = shift;
$log //= '/var/log/nginx/access.log.1';
return $log;
}
sub setignore {
my $ignore = shift;
$ignore //= '/var/lib/nginx/ignore';
return $ignore;
}
sub checkemail {
my $email = shift;
unless (defined $email) {
print "Email address is compulsory\n";
pod2usage(-verbose => 1) && exit;
}
my $valid = Email::Valid->address($email);
return if $valid;
print "Email address ->$email<- is invalid\n";
pod2usage(-verbose => 1) && exit;
}
sub checkserver {
my $server = shift;
unless (defined $server) {
print "Outbound email server is compulsory\n";
pod2usage(-verbose => 1) && exit;
}
}
sub parselog {
my ($fh) = @_;
for my $line (<$fh>) {
parseline($line);
}
}
sub parseline {
my ($line) = @_;
say "Log entry = ->$line<-" if $opt_debug;
my ($ip) = $line =~ m/^($RE{net}{IPv4})/;
return unless $ip;
say "IP address = ->$ip<-" if $opt_debug;
return if $ignore =~ m/$ip;/m;
$ignore .= $ip . ';';
$report .= "\n$line";
my $whois = Net::Whois::IP::whoisip_query($ip);
print Dumper $whois if $opt_debug;
for my $key (qw(source country netname person org-name e-mail phon
+e origin inetnum Country NetName OrgAbuseName OrgName OrgAbuseEmail O
+rgAbusePhone OriginAS NetRange)) {
$report .= "$key: $$whois{$key}\n"
if defined $$whois{$key};
}
}
sub sendreport {
my $transport = Email::Sender::Transport::SMTP->new({
host => $opt_server,
port => 25,
});
my $subject = 'Gogs access logs clear';
$subject = 'Unexpected gogs access attempts' if $report;
my $body = $report;
$body //= '';
Email::Stuffer->to($opt_email)
->from('nginxlog@davies.systems')
->subject($subject)
->text_body($body)
->transport($transport)
->send;
}
=pod
=head1 NAME
Nginxlog.pm
=head1 SYNOPSIS
perl Nginxlog.pm --email invalid@example.com --server sendmail.exa
+mple.com options
=head1 DESCRIPTION
This modulino parses nginx access logs for signs of inappropriate
access.
=head1 ARGUMENTS
All arguments can be specified with a single minus sign and the
first letter if preferred. Two are compulsory, the rest have
defaults.
--email The destination email address for the report.
--email or -e IS COMPULSORY.
--server The outbound email server
--server or -s IS COMPULSORY.
--log The location of the log file. Defaults to
/var/log/nginx/access.log.1. This implies that the
run will take place after logrotate has opened a new
log file, meaning that the modulino will not interfere
with or be confused by actions in progress.
--ignore The location of a file containing IP addresses that ca
+n
safely be ignored. Defaults to
/var/lib/nginx/ignore. This must be maintained manuall
+y.
=head1 OPTIONS
--help Print Usage, Arguments & Options
--man Print entire POD
--versions Print Modules, Perl, OS, Program info
--debug Print debugging information
=head1 AUTHOR
John Davies
=head1 CREDITS
https://www.perlmonks.org/?node_id=155288
https://perlmaven.com/modulino-both-script-and-module
=head1 BUGS
None TTBOMK.
=head1 UPDATES
0.0.1 First development version
=cut
1;