maillog.pl -C conf_file.conf
This monitors logfiles, run system commands from a configuration file.
It uses GPG encryption, it may be used to securely monitor remote hosts.
I wrote it to monitor clients machines remotely, most existing scripts or programs I've seen don't offer encryption, which is absolutely required if you are going to transfer logs and other sensitive system information across email channels.
It is also useful if you need to run cron processes that may output sensitive information.
The second reason, as pointed out here:
http://www.jammed.com/~jwa/hacks/security/checksyslog/checksyslog-doc.html
Mr. Abendschan states:
There are at least two ways to scan your system logs for security problems or
other errors. One is to grep for data you know is indicative of a problem ..
"refused connect from", "REPEATED LOGIN FAILURES", "STOR .rhosts". This method
requires you to generate huge lists of things you think are "suspicious
behaviour" -- lists which are by their nature incomplete; you need to know that
a problem exists in order to detect it.
Most stuff I've seen involve grepping log files for key words, this package can do that too, however, it's really designed to grep out patterns it does NOT know about. The idea is to train it to ignore the usual stuff, but show the unusual.
The configuration syntax is meant to be ``shell like'' so that it'll work with editors supporting ``shell'' syntax highlighting. Words need to be quoted if they have spaces, and __HERE documents are supported, similar to shell script here documents.
The general format is
command argument "argument two"
Some commands accept (require actually) sub-sections, these subsections are accomplished with __HERE documents.
command <<__HERE
Configuration local to "command"
__HERE
It looks rather ugly, I know.. but, if you've ever worked with __HERE docs, it's pretty clear that anything up-to __HERE is intended for 'command'.
Comments are either hash marks or <<__HERE documents that point to no command. This way, you can easily comment out large chunks.
# This is a comment. <<__EOC
This is also a comment
__EOC
Pretty much standard core modules, with a few exceptions:
It uses Net::SMTP to send email.
It uses Digest::MD5 for file monitoring.
alias <<__HERE
command
command
command
__HERE
TODO: write about Modules and the ReportEntry package.
command <<__EOC
exec 'ls -l'
description "Show directory"
mime_file "ls.txt"
__EOC
Options for command are:
ls -l
ls.txt will be the attachment filename, (Each command is a separate mime attachment)
Shows up as the Content-Description in the MIME data.
They can then modify files and merely update the list of checksums to reflect their changes, making the whole thing a moot point.
This is why you're supposed to keep the list on another server, but if you are working remotely, this can be a real hassle. (And sending a complete directory listing in the clear through email isn't much better)
That is where this module comes in.
This module allows you to monitor file stamps. It will create an MD5 digest of all files in the specified directories and email you a listing.
The solution presented here is to email a PGP attachment of these checksums, then compare with a local known copy, looking for differences.
Multiple dir statements are possible, or, you can just put them all in one.
dir /patha
dir /pathb
dir /pathc
Is the same as
dir /path1 /pathb /pathc
The directory is searched recursively.
monitor <<__EOM
mime_file "monitor.txt"
description "MD5 sum of files"
dir /etc
dir /opt
dir /usr/bin /usr/lib /usr/local /usr/sbin
__EOM
You can save the results and then compare them with future listings by running logmail.pl with the --check /path/to/good_listing.log
The output format is
b64digest<tab>$digest_key<tab>size<tab>filename warn<tab>Error message about one of the files.
$op can be =~ or !~ meaning ``match'' ``don't match''
example
ignore =~ /MARK/ ignore =~ /Another/
Multiple ignore commands are OK, unless they appear within do .. end, they are OR'd. (Above ignores lines containing /MARK/ AND /Another/)
An example shows it best
do ignore
=~ /pattern/
=~ /pattern 2/
end
This literally creates this:
sub { ($_ =~ /pattern/) && ($_ =~ /pattern 2/) }
Which says, Ignore lines that contain BOTH /pattern/ and /pattern 2/
You can do the same with filter
do filter
=~ /THIS/
=~ /AND/
=~ /THAT/
end
Copyright (C) Jamie Hoglund 2005
It's free software, same conditions as Perl itself.
I'm always available for custom programming, system maintenance and monitoring. Please contact me for details. Your business helps keep these things available, it would be a pleasure to work with you.
http://www.geniegate.com/contact.php
Read this section if you want to write your own modules, this section is more for developers than end users.
It is possible to write custom modules by extending the ReportEntry package, providing
your own run() method. This section is hardly complete, I'll just give the basics.
You've probably noticed, everything is one huge perl file. This makes the code harder to maintain but this way it's not terribly difficult to install on multiple hosts.
package MyModule;
use strict;
our(@ISA) = qw(ReportEntry);
sub run {
my($self,$out) = @_;
my $enc = $self->encrypt_cmd();
open(P,"ls | $enc |");
while(<P>){
$out->print($_);
}
close(P);
}
1;
In your configuration, you would add something like this:
module MyModule custom /path/to/MyModule.pm
After you do this, a new instance of your module will be created each time
a custom command is encountered in the configuration.
custom <<__MY_MODULE
directive_a "argument"
...
directive_b "argument"
__MY_MODULE
Your module will have directive_a and directive_b available to C<$self>.
run($obj)print() method.
Your module should then print with that object, $obj->print() to have your
information inserted into the current MIME attachment.
You'll have to encrypt the information yourself however. Use $self->encrypt_cmd()
to get the PGP program name.
parse()parse($rdr) method.
where $rdr is an object that supports a getline() method. (as well as interpret(T|F)
and words($str).) Simply call $rdr->getline() to fetch the next line in your
configuration.
You can call $rdr-interpret(0)> if you need the literal data.
You can call @words = $rdr-words($string)> to have Text::ParseWords::shellwords()
parse a line for you.
Unless your module needs this, the default parse() method should suffice.
validate()validate() method if your module requires certain things. In this
method, you could test for required parameters, etc.. and die() with a message if they
aren't present.
sub validate {
my($self) = shift;
$self->{directive_a} || die "Missing directive_a!";
}
finish()run() but before DESTRUCT, this is the
place to do it. (Not normally used)
encrypted()encrypt_cmd()pgp command, either the local one or a global one if
there is no local pgp setting.
As pointed out before, the data isn't automatically encrypted, you need to run it through a pipe yourself.
mime_file()
Returns $self->{mime_file}
description()
Returns $self->{description}