||Taming periodic emails
How to get FreeBSD's periodic to send you email only when you need to be alerted.
FreeBSD's periodic(8) keeps an eye on your server for you, and runs a number of security and system checks. When these checks run, they'll generate an email detailing what checks were run.
The problem is that most of the time, the emails simply tell you that the checks were run, and found everything to be okay. This makes detecting alerts from periodic far more difficult.
In order to solve this problem, we can use swatch (The Simple WATCHer and filter) to divide the emails into sections, and then ignore any sections that are not reporting problems.
- Install swatch from ports
$ su - root
root# cd /usr/ports/security/swatch/
root# make install
root# make clean
- Send periodic emails to a separate file by creating an alias and changing the address the mail is sent to
- create the new mailbox:
root# touch /var/log/periodic
root# chown mailnull:mailnull /var/log/periodic
root# chmod 600 /var/log/periodic
- insert the following into /etc/mail/aliases:
If you wish to save a copy of all the periodic email unmolested, then you can create the following entry instead:
(don't forget to touch, chown and chmod the periodic.archive if you do this)
periodic: /var/log/periodic, /var/log/periodic.archive
- Rebuild the aliases database
- Add the following entries to periodic.conf:
- Setup a config file for swatch. Let's call it /usr/local/etc/parse-periodic.conf
# Swatch config for periodic(8) on FreeBSD 5.4
# I use a default echo, and then ignore all the stuff I don't want. This way
# I'm only rejecting known messages.
# CMD: swatch --input-record-separator="\n\n" -f $TMPFILE -c /usr/local/etc/parse-periodic.conf
# With --input-record-separator="\n\n" set, Perl will be put into "Paragraph Mode"
# See swatch(1) for more information about the sentax of this file
# Get rid of the email headers
ignore /^From \S*\@\S*.\S\S+/
# The \z matches the end of the string (So items with someting listed
# will *not* match)
ignore /Removing stale files from \/var\/(preserve|rwho):\z/
ignore /Cleaning out old system announcements:\z/
ignore /Backup passwd and group files:\z/
ignore /Verifying group file syntax:\n\/etc\/group is fine\z/
ignore /Backing up mail aliases:\z/
ignore /Rotating accounting logs and gathering statistics:\z/
ignore /Verifying group file syntax:\z/
# The number and location of all the \n's are important.
# That way, we'll only match filesystems that we don't expect to be
# at 100%
# Header |-------- / -------| |---- /dev ---| |-------- /tmp ---------| |-------- /usr ---------| |-------- /var ---------| |---- /proc --| |----------- Linux proc -----------|
ignore /Disk status:\n.*\n.*\s[0-8]?[0-9]%\s+\/\n.*100%\s+\/dev\n.*\s[0-8]?[0-9]%\s+\/tmp\n.*\s[0-8]?[0-9]%\s+\/usr\n.*\s[0-8]?[0-9]%\s+\/var\n.*100%\s+\/proc\n.*100%\s+\/usr\/compat\/linux\/proc\z/
ignore /Disk status:\n.*\n.*\s[0-8]?[0-9]%\s+\/\n.*100%\s+\/dev\n.*\s[0-8]?[0-9]%\s+\/tmp\n.*\s[0-8]?[0-9]%\s+\/usr\n.*\s[0-8]?[0-9]%\s+\/var\n.*100%\s+\/proc\z/
# Without /proc (it's not always mounted)
ignore /Disk status:\n.*\n.*\s[0-9][0-9]?%\s+\/\n.*100%\s+\/dev\n.*\s[0-9][0-9]?%\s+\/tmp\n.*\s[0-9][0-9]?%\s+\/usr\n.*\s[0-9][0-9]?%\s+\/var\z/
ignore /Last dump\(s\) done \(Dump \'>\' file systems\):\z/
# We only care about the errors on the interfaces
# Header Name MTU Net Addr Ipkts Ierrs Opkts Oerrs Coll
ignore /Network interface status:\n.*\n\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(0|-)\s+\S+\s+(0|-)\s+(0|-)/
# Only show the load average only if the 15 minute average is more then 1
ignore /Local system status:\n.*load averages: [0-9].[0-9][0-9], [0-9].[0-9][0-9], 0.[0-9][0-9]/
ignore /\/var\/spool\/(clientmqueue|mqueue) is empty/
ignore /\(output mailed separately\)/
ignore /Checking for rejected mail hosts:\z/
ignore /Checking for denied zone transfers \(AXFR and IXFR\):\z/
ignore /-- End of (daily|weekly|security|monthly) output --/
ignore /Checking for uids of 0:\nroot 0\ntoor 0\z/
ignore /Checking for passwordless accounts:\z/
ignore /<YOUR-HOSTNAME> login failures:\z/
ignore /<YOUR-HOSTNAME> refused connections:\z/
ignore /Checking setuid files and devices:\z/
ignore /Cleaning up kernel database files:\z/
ignore /Rebuilding (locate|whatis) database:\z/
ignore /Doing login accounting:/
# Print everything else
mail addresses=root,subject="Periodic Mail"
You'll likely need to adjust some of the entries in the file (notably the filesystem section) and you'll want to replace <YOUR-HOSTNAME> with your actual hostname.
Don't worry if you don't know what to put in right now. The config will err on the side of sending you too much email. It will be easy to adjust the regular expressions once you see what it is not filtering.
- Next, create a small script to parse /var/log/periodic. In this example the script is called "~root/bin/parse-periodic".
Don't forget to make the script executable:
# What is the file we want to look at?
mv $INFILE $TMPFILE
chown mailnull:mailnull $INFILE
chmod 600 $INFILE
/usr/local/bin/swatch --input-record-separator="\n\n" -f $TMPFILE -c /usr/local/etc/parse-periodic.conf && \
chmod 500 ~root/bin/mailparser
- Last, add a cronjob to process /var/log/periodic daily
You may want to change the time in the crontab - depending on when periodic runs on your machine
45 6 * * * root ~/bin/mailparser > /dev/null
Tomorrow morning when you wake up, you shouldn't see any email from periodic - unless of course there is something for you to look at.
To give it a test, change your password. The periodic security checks will detect that the password file has changed, and you should get an email stating that.
If the config file is letting through too much email, then you can adjust is as necessary. Be careful however that you don't accidently match too much, or you might not be alerted when there's something that you should be aware of.
© 2007 O'Reilly Media, Inc.
| Customer Service:
| Book issues:
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.