MailLog - Remote reporting tool with PGP support.

NAME

maillog.pl -C conf_file.conf


DESCRIPTION

This monitors logfiles, run system commands from a configuration file.

It uses GPG encryption, it may be used to securely monitor remote hosts.

--generate
Generate a new config file, dump to standard output.

-C ``file.conf''
Use configuration ``file.conf''

--conf=``file.txt''
Same as -C, this is required - It uses no default configuration.

--check /prior/values.md5
Check monitored files against a local log. (Probably should use a more convenient program for that)


PURPOSE

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.


CONFIGURATION

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


DEPENDENCIES

Pretty much standard core modules, with a few exceptions:

It uses Net::SMTP to send email.

It uses Digest::MD5 for file monitoring.


COMMANDS

source ``filename''
Source a config file. Useful if you want to include one configuration into another. Not useful if you're trying to diagnose a problem..

set key value
Change a global setting. Settings are
pgp ``gpg -a -e -r 'you@example.com' --no-tty --batch''
The PGP program used for encryption. This must exist and slurp all it's input in before outputting anything.

to 'you@example.com'
Who email is sent to.

from 'you@example.com'
Who email is sent to.

smtp ``smtp.example.com''
SMTP server for email.

debug
Turn debugging on/off

subject
Set the subject line.

host
Fake the host name. (Not required)

module Perl::Package alias /path/to/file.pm
Load a perl module. The module can then be used as
  alias <<__HERE
                command
                command
                command
  __HERE

TODO: write about Modules and the ReportEntry package.


BUILT IN MODULES

command - Execute commands.
Lets you execute commands and mail results. (Like cron, but with PGP)
        command <<__EOC
                exec 'ls -l'
                description "Show directory"
                mime_file "ls.txt"
        __EOC

Options for command are:

exec ``command name''
  ls -l
mime_file ``attachment_filename.txt''

  ls.txt will be the attachment filename, (Each command is a separate mime attachment)
description ``Mime description''
  Shows up as the Content-Description in the MIME data.
monitor
One of the problems with monitoring a checksum of your files is that if an attacker breaks in, they might have access to your database of checksums.

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.

dir /patha /pathb /pathc
Specify which directories to monitor. Only regular files will be checked. (It won't try to do an md5sum on a pipe)

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.

example

        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.
watch - Watch a log file.
This will scan log files, weeding out regex's that you're not interested in.
logfile ``/path/to/logfile.txt''
The file to watch.

mime_file ``local.txt''
Attachment filename

description ``Description''
The Content-Description field.

ignore $op /Pattern/
This needs some explaining.. the ignore tells it which patterns to ignore, it's VERY important to note that /Pattern/ is actually perl code, and will be compiled into a subroutine. So, the syntax has to be valid perl syntax.

$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/)

filter $op /Pattern/
This turns on filtering, not recommended.. But if you do use it, it will only show lines matching the pattern. (Multiple filters can be supplied, like ignore)

trim 'yes'
If set to 'yes' spaces will be trimmed off either side of each line. Some logfiles like to pad the ends with spaces, which can confuse patterns.

do (ignore|filter) ... end
do .. end allow you to group multiple patterns into AND cases.

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

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


MODULES

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)
This method is the only critical one, it must accept as a parameter an object that with a 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()
If you need to do your own parsing, you can overload the 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()
You should overload the 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()
If you need to do special things after the run() but before DESTRUCT, this is the place to do it. (Not normally used)

encrypted()
This returns TRUE usually. If your data is not encrypted, overload this and return a FALSE value.

encrypt_cmd()
Returns the 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}