Bug#406206: [Pkg-aide-maintainers] Bug#406206: aide: AIDE cronjob
gives 'onexit: command not found' error
Marc Haber
mh+debian-packages at zugschlus.de
Fri Jan 12 17:33:42 CET 2007
On Thu, Jan 11, 2007 at 01:42:02PM +0100, Marc Haber wrote:
> Addressing this issue was a little harder than actually expected and
> resulted in a major refactoring of the daily cron job code. Can you
> please try with the attached new cron script which I have commited to
> svn?
That version was still buggy. New version attached.
Greetings
Marc
--
-----------------------------------------------------------------------------
Marc Haber | "I don't trust Computers. They | Mailadresse im Header
Mannheim, Germany | lose things." Winona Ryder | Fon: *49 621 72739834
Nordisch by Nature | How to make an American Quilt | Fax: *49 621 72739835
-------------- next part --------------
#!/bin/bash
set -e
set -C
# trap handler
traphandler() {
trap - INT ERR
if [ -n "${LOCKED:-}" ]; then
# we have the lock,
pidof aide | xargs --no-run-if-empty kill -9
fi
onexit signal $1
return 0
}
trap ' traphandler INT; trap - INT ERR' INT
trap ' traphandler ERR; trap - INT ERR' ERR
# bail if no aide binary found
[ -f "/usr/bin/aide" ] || exit 0
# default variables
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
LOGDIR="/var/log/aide"
# LOGFILE: /var/log/aide/aide.log - all logs untruncated (not temp)
LOGFILE="$LOGDIR/aide.log"
CONFFILE="/var/lib/aide/aide.conf.autogenerated"
PREFIX="aide"
TMPBASE="/var/run/aide"
LOCKFILE="$TMPBASE/cron.daily.lock"
TMPDIRIN="$TMPBASE/cron.daily"
AIDEARGS="-V4"
FQDN="$(hostname -f)"
MAILSUBJ="Daily AIDE report for $FQDN"
DATE="$(date +"%Y-%m-%d %H:%M")"
BEGINSTAMP="$(date +"%Y-%m-%d %H:%M:%S")"
# have /etc/default/aide override variables
if [ -f "/etc/default/aide" ]; then
. "/etc/default/aide"
fi
# from here on, we're going to bail on unbound variables
set -u
# umask
umask 077
# grep aide configuration data from aide config
DATABASE="$(< "$CONFFILE" grep "^database=file:/" | head -n 1 | cut --delimiter=: --fields=2)"
DATABASE_OUT="$(< "$CONFFILE" grep "^database_out=file:/" | head -n 1 | cut --delimiter=: --fields=2)"
# default values
MAILTO="${MAILTO:-root}"
eval MAILTO="$MAILTO"
DATABASE="${DATABASE:-/var/lib/aide/aide.db}"
LINES="${LINES:-1000}"
COMMAND="${COMMAND:-check}"
COPYNEWDB="${COPYNEWDB:-no}"
QUIETREPORTS="${QUIETREPORTS:-no}"
ONEXIT=""
# functions
mytempfile() {
NAME="$1"
echo "$TMPDIR/$NAME"
touch "$TMPDIR/$NAME"
}
frame() {
WIDTH=78
STARS="*******************************************************************************"
SPACES=" "
printf "%s\n" "${STARS:1:$WIDTH}"
while read line ; do
HALF="${SPACES:1:$((($WIDTH-${#line})/2))}"
LINE="$HALF$line$SPACES"
printf "*%s*\n" "${LINE:1:$(($WIDTH-2))}"
done
printf "%s\n" "${STARS:1:$WIDTH}"
}
onexit() {
if [ "$ONEXIT" = "running" ]; then
return 1
fi
ONEXIT="running"
local LOGHEAD
local MAILHEAD
case "$1" in
signal)
LOGHEAD="$(printf "terminated with signal %s" "$2")"
MAILHEAD="$(printf "The cron job was terminated with signal %s" "$2")"
;;
fatal)
LOGHEAD="$(printf "terminated by fatal error.")"
MAILHEAD="$(printf "The cron job was terminated by a fatal error.")"
;;
nolock)
LOGHEAD="$(printf "terminated because lock %s could not be obtaiend." "$LOCKFILE")"
MAILHEAD="$(printf "The cron job was terminated because lock %s could not be obtained." "$LOCKFILE")"
;;
cantmovetmp)
LOGHEAD="$(printf "terminated: Cannot move away %s." "$TMPDIRIN")"
MAILHEAD="$(printf "The cron job was terminated: Cannot move away %s." "$TMPDIRIN")"
;;
cantcreatetmp)
LOGHEAD="$(printf "terminated: Cannot create temporary directory %s." "$TMPDIRIN")"
MAILHEAD="$(printf "The cron job was terminated: Cannot create temporary directory %s." "$TMPDIRIN")"
;;
success)
;;
*)
LOGHEAD="$(printf "wrong parameter (\"%s\") to onexit." "$1")"
MAILHEAD="$(printf "The cron job was terminated for unknown reasons, and a wrong parameter (\"%s\")was given to onexit." "$1")"
;;
esac
if [ -z "${TMPDIR:-}" ] || [ -z "${MAILFILE:-}" ]; then
# we are being called so early that we are not yet fully initialized
# LOGHEAD goes to syslog instead of LOGFILE since we do not know
# what's up with LOGFILE
logger -t aide-cron-daily "$LOGHEAD"
echo "$MAILHEAD" | /usr/bin/mail -s "premature termination - $MAILSUBJ" "$MAILTO"
else
# we are being called after the cron job was properly set up.
# To the full works.
[ -f "$LOGFILE" ] && savelog -t -g adm -m 640 -u root -c 7 "$LOGFILE" > /dev/null
printf >> "$MAILFILE" \
"This is an automated report generated by the Advanced Intrusion Detection
Environment on %s started at %s.\n\n" "$FQDN" "$BEGINSTAMP"
printf >> "$LOGFILE" \
"aide run on %s started at %s.\n" "$FQDN" "$BEGINSTAMP"
if [ -n "$LOGHEAD" ]; then
printf "$LOGHEAD\n" | frame >> "$LOGFILE"
printf "\n" >> "$LOGFILE"
fi
if [ -n "$MAILHEAD" ]; then
printf "$MAILHEAD\n" | frame >> "$MAILFILE"
printf "\n\n" >> "$MAILFILE"
fi
# report about AIDE's return value
if [ -n "${ARETVAL:-}" ]; then
ARETEXPL=""
ARETERR=""
PREFIX="$(printf "AIDE returned with a %d exit code. " "$ARETVAL")"
case "$ARETVAL" in
-1)
PREFIX=""
ARETERR="the cron job was interrupted before AIDE could return an exit code.";;
0)
PREFIX="AIDE returned with a zero exit code. "
ARETEXPL="No changes detected!";;
1)
ARETEXPL="New files detected!";;
2)
ARETEXPL="Removed files detected!";;
3)
ARETEXPL="New and removed files detected!";;
4)
ARETEXPL="Changed files detected!";;
5)
ARETEXPL="New and changed files detected!";;
6)
ARETEXPL="New and deleted files detected!";;
7)
ARETEXPL="New, deleted and changed files detected!";;
14)
ARETERR="Error writing!";;
15)
ARETERR="Invalid Argument!";;
16)
ARETERR="Unimplemented function!";;
17)
ARETERR="Invalid configuration!";;
18)
ARETERR="Input/Output error!";;
default)
ARETERR="$(printf "AIDE returned an unknown non-zero exit value\nexit value is %d\n\n" "$ARETVAL")";;
esac
if [ -n "$ARETEXPL" ]; then
echo "$PREFIX $ARETEXPL" >> "$MAILFILE"
echo "$PREFIX $ARETEXPL" >> "$LOGFILE"
fi
if [ -n "$ARETERR" ]; then
echo "$PREFIX $ARETERR" | frame >> "$MAILFILE"
echo "$PREFIX $ARETERR" | frame >> "$LOGFILE"
fi
unset ARETEXPL
unset ARETERR
unset PREFIX
else
ARETEXPL="ARETVAL not initialized. cron job was aborted prematurely."
echo $ARETEXPL | frame >> "$LOGFILE"
echo $ARETEXPL | frame >> "$MAILFILE"
unset ARETEXPL
printf "\n" >> "$LOGFILE"
printf "\n\n" >> "$MAILFILE"
fi
# script errors
if [ -n "${ERRORLOG:-}" ] && [ -s "$ERRORLOG" ]; then
printf "script errors\n" | frame >> "$MAILFILE"
< "$ERRORLOG" cat >> "$MAILFILE"
printf "End of script errors\n\n" >> "$MAILFILE"
printf "script errors\n" | frame >> "$LOGFILE"
< "$ERRORLOG" cat >> "$LOGFILE"
printf "End of script errors\n" >> "$LOGFILE"
fi
# aide post run information
if [ -n "${POSTRUNLOG:-}" ] && [ -s "$POSTRUNLOG" ]; then
printf "AIDE post run information\n" >> "$MAILFILE"
< "$POSTRUNLOG" cat >> "$MAILFILE"
printf "End of AIDE post run information\n\n" >> "$MAILFILE"
printf "AIDE post run information\n" >> "$LOGFILE"
< "$POSTRUNLOG" cat >> "$LOGFILE"
printf "End of AIDE post run information\n" >> "$LOGFILE"
fi
# include error log in daily report e-mail
if [ -n "${AERRLOG:-}" ] && [ -s "$AERRLOG" ]; then
errorlines="$(wc -l "$AERRLOG" | awk '{ print $1 }')"
if [ "${errorlines:=0}" -gt "$LINES" ]; then
printf "AIDE has returned many errors.\nthe error log output has been truncated in this mail\n" | \
frame >> "$MAILFILE"
printf >> "$MAILFILE" "Error output is %d lines, truncated to %d.\n" "$errorlines" "$LINES"
< "$AERRLOG" head -n "$LINES" >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of truncated AIDE error output. The full output can be found in %s.\n\n" "$LOGFILE"
else
printf >> "$MAILFILE" "Errors produced (%d lines):\n" "$errorlines"
< "$AERRLOG" cat >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of AIDE error output.\n\n"
fi
printf >> "$LOGFILE" "AIDE error output (%d lines):\n" "$errorlines"
< "$AERRLOG" cat >> "$LOGFILE"
printf >> "$LOGFILE" "End of AIDE error output\n"
else
printf >> "$MAILFILE" "AIDE produced no errors.\n\n"
printf >> "$LOGFILE" "AIDE produced no errors.\n"
fi
# include de-noised log into mail
if [ -n "${ARUNLOG:-}" ] && [ -s "$ARUNLOG" ]; then
if [ -n "${NOISE:-}" ]; then
NOISETMP="$(tempfile --directory "/tmp" --prefix "aidenoise")"
NOISETMP2="$(tempfile --directory "/tmp" --prefix "aidenoise")"
< "$ARUNLOG" sed -n '1,/^Detailed information about changes:/p' | \
grep '^\(changed\|removed\|added\):' | \
grep -v "^added: THERE WERE ALSO [0-9]\+ FILES ADDED UNDER THIS DIRECTORY" > "$NOISETMP2"
if [ -n "$NOISE" ]; then
< "$NOISETMP2" grep -v "^\(changed\|removed\|added\):$NOISE" > "$NOISETMP"
printf >> "$MAILFILE" "De-Noised output removes everything matching %s.\n" "$NOISE"
fi
if [ -s "$NOISETMP" ]; then
loglines="$(< $NOISETMP wc -l | awk '{ print $1 }')"
if [ "${loglines:=0}" -gt "$LINES" ]; then
printf "AIDE has returned long output which has been truncated in this mail\n" | \
frame >> "$MAILFILE"
printf >> "$MAILFILE" \
"De-Noised output is %d lines, truncated to %d.\n" "$loglines" "$LINES"
< "$NOISETMP" head -n "$LINES" >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of truncated De-Noised AIDE output. The full output can be found in %s.\n\n" "$LOGFILE"
else
printf >> "$MAILFILE" "De-Noised output of the daily AIDE run (%d lines):\n" "$loglines"
< "$NOISETMP" cat >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of De-Noised AIDE output.\n\n"
fi
else
printf >> "$MAILFILE" "AIDE detected no changes after removing noise.\n\n"
fi
printf >> "$MAILFILE" "============================================================================\n"
fi
# include non-de-noised log into mail
if [ -n "${ARUNLOG:-}" ] && [ -s "$ARUNLOG" ]; then
loglines="$(wc -l "$ARUNLOG" | awk '{ print $1 }')"
if [ "${loglines:=0}" -gt "$LINES" ]; then
printf "AIDE has returned long output which has been truncated in this mail\n" | \
frame >> "$MAILFILE"
printf >> "$MAILFILE" \
"Output is %d lines, truncated to %d.\n" "$loglines" "$LINES"
< "$ARUNLOG" head -n "$LINES" >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of truncated AIDE output. The full output can be found in %s.\n\n" "$LOGFILE"
else
printf >> "$MAILFILE" "Output of the daily AIDE run (%d lines):\n" "$loglines"
< "$ARUNLOG" cat >> "$MAILFILE"
printf >> "$MAILFILE" "\nEnd of AIDE output.\n\n"
fi
printf >> "$LOGFILE" "AIDE output (%d lines):\n" "$loglines"
< "$ARUNLOG" cat >> "$LOGFILE"
printf >> "$LOGFILE" "End of AIDE output.\n\n"
else
printf >> "$MAILFILE" "AIDE detected no changes.\n\n"
printf >> "$LOGFILE" "AIDE detected no changes.\n\n"
fi
else
printf >> "$MAILFILE" "funny, AIDE did not leave a log.\n\n"
printf >> "$LOGFILE" "funny, AIDE did not leave a log.\n"
fi
if [ -n "${DBCHECKLOG:-}" ] && [ -s "$DBCHECKLOG" ]; then
< "$DBCHECKLOG" cat >> "$MAILFILE"
printf >> "$MAILFILE" "\n"
< "$DBCHECKLOG" cat >> "$LOGFILE"
fi
printf >> "$MAILFILE" "End of AIDE daily cron job at %s, run time %d seconds\n" "$(date +"at %Y-%m-%d %H:%M")" "$(( $(date +%s) - $BEGINTIME ))"
printf >> "$LOGFILE" "End of AIDE daily cron job at %s, run time %d seconds\n" "$(date +"at %Y-%m-%d %H:%M")" "$(( $(date +%s) - $BEGINTIME ))"
# send mail if changes or errors were detected or quiet reports not requested
if [ "$QUIETREPORTS" = "no" ] || [ "$CHANGES" != "0" ] || [ $(< "$ERRORLOG" wc -l) -ne 0 ]; then
< "$MAILFILE" /usr/bin/mail -s "$MAILSUBJ" "$MAILTO"
fi
# clean up temp files
rm -rf $TMPDIR
fi
# clear lock
if [ -n "${LOCKED:-}" ] && command -v dotlockfile >/dev/null 2>&1; then
dotlockfile -u "$LOCKFILE" || true
fi
unset LOCKED
return 0
}
BEGINTIME="$(date +%s)"
if command -v dotlockfile >/dev/null 2>&1; then
if ! dotlockfile -p -l "$LOCKFILE"; then
onexit nolock
exit 1
fi
else
PREERRLOG="no dotlockfile binary in path, not checking for already running aide cron job\n"
fi
LOCKED=yes
# prepare temp dir
if [ -e "$TMPDIRIN" ]; then
if ! NEWNAME="$(mktemp -d $TMPBASE/cron.daily.old.XXXXXXXXXX)"; then
onexit cantmovetmp
exit 1
fi
mv "$TMPDIRIN" "$NEWNAME"
unset NEWNAME
OLDTMPDIRFOUND="yes"
fi
if ! mkdir -p $TMPDIRIN; then
onexit cantcreatetmp
exit 1
fi
# we can now directly use file names inside $TMPDIR: It is only
# writeable for us (umask 077), so we're safe against symlink attacks.
# We use invariant file names here since our work files need to be
# excluded from aide.
TMPDIR="$TMPDIRIN"
# now, with $TMPDIR having been created, we can use onexit.
# ERRORLOG: Error messages from script. Gets written to $LOGFILE first
ERRORLOG="$(mytempfile errorlog)"
if [ -n "${PREERRORLOG:-}" ]; then
printf >> "$ERRORLOG" "$PREERRORLOG"
fi
unset PREERRORLOG
# MAILFILE: Contents gets mailed. Built and handled from inside onexit()
MAILFILE="$(mytempfile mailfile)"
# aide return value
ARETVAL=-1
if [ ! -f "$DATABASE" ]; then
printf >> "$ERRORLOG" "Fatal error: The AIDE database does not exist!\n"
printf >> "$ERRORLOG" "This may mean you haven't created it, or it may mean that someone has removed it.\n"
onexit fatal
exit 0
fi
# code
# re-assign current time to be more accurate about aide's real start time
BEGINSTAMP="$(date +"%Y-%m-%d %H:%M:%S")"
# ARUNLOG: standard output of aide run
ARUNLOG="$(mytempfile arunlog)"
# AERRLOG: standard error of aide run
AERRLOG="$(mytempfile aerrlog)"
printf "begin timestamp %s\n" "$BEGINSTAMP" >> "$ARUNLOG"
update-aide.conf
aide.wrapper $AIDEARGS "--$COMMAND" >|"$ARUNLOG" 2>|"$AERRLOG" && ARETVAL="$?"
ARETVAL="$?"
# POSTRUNLOG: summary of aide execution and cron job log
POSTRUNLOG="$(mytempfile postrunlog)"
# DBCHECKLOG: Output of the database checksums
DBCHECKLOG="$(mytempfile dbchecklog)"
# NOISETMP: completely de-noised log
# NOISETMP2: pre-filtered ARUNLOG, containing only changed, removed and added lines
NOISETMP="$(mytempfile noisetmp)"
NOISETMP2="$(mytempfile noisetmp2)"
# find out checksums and other data for the database we were comparing against.
DBCHECKDB="$(mytempfile dbcheckdb)"
printf >> "$DBCHECKLOG" "The check was done against %s with the following characteristics:\n" "$DATABASE"
DBCHECKFILE="$DBCHECKDB"
DBCHECKOLD="$DATABASE"
DBCHECKNEW="$DBCHECKDB"
printf "database=file:%s\ndatabase_out=file:%s\n%s\$ p+u+g+n+i+s+b+m+c+md5+sha1+rmd160+haval+gost+crc32+tiger" \
"${DBCHECKOLD}" "${DBCHECKNEW}" "${DBCHECKFILE}" | \
aide --config=- --init > /dev/null
sed -i "s|^${DBCHECKFILE}[[:space:]]|${DATABASE} |" "$DBCHECKDB"
DBCHECKFILE="$DATABASE"
DBCHECKOLD="$DBCHECKDB"
DBCHECKNEW="$DBCHECKDB"
printf "database=file:%s\ndatabase_out=file:%s\n%s\$ p+u+g+n+i+s+b+m+c+md5+sha1+rmd160+haval+gost+crc32+tiger" \
"${DBCHECKOLD}" "${DBCHECKNEW}" "${DBCHECKFILE}" | \
aide --config=- --check 2>/dev/null | \
sed -n '/^ \(Size\|Bcount\|Mtime\|Ctime\|Inode\|MD5\|SHA1\|RMD160\|TIGER\|CRC32\|HAVAL\|GOST\)/{s/\([^:]*\)[^,]*,[[:space:]]*\(.*\)/\1: \2/;p;}' \
>> "$DBCHECKLOG"
if [ "$COMMAND" = "update" ]; then
printf >> "$DBCHECKLOG" "\nThe AIDE run created a new database %s with the following characteristics:\n" "$DATABASE_OUT"
sed -i "s|^${DATABASE}[[:space:]]|${DATABASE_OUT} |" "$DBCHECKDB"
DBCHECKFILE="$DATABASE_OUT"
DBCHECKOLD="$DBCHECKDB"
DBCHECKNEW="$DBCHECKDB"
printf "database=file:%s\ndatabase_out=file:%s\n%s\$ p+u+g+n+i+s+b+m+c+md5+sha1+rmd160+haval+gost+crc32+tiger" \
"${DBCHECKOLD}" "${DBCHECKNEW}" "${DBCHECKFILE}" | \
aide --config=- --check 2>/dev/null | \
sed -n '/^ \(Size\|Bcount\|Mtime\|Ctime\|Inode\|MD5\|SHA1\|RMD160\|TIGER\|CRC32\|HAVAL\|GOST\)/{s/\([^:]*\)[^,]*,[[:space:]]*\(.*\)/\1: \2/;p;}' \
>> "$DBCHECKLOG"
fi
rm -f "$DBCHECKDB"
# find out whether we neeed to copy the new database over the old one
COPYDB="0"
if [ "$COPYNEWDB" = "ifnochange" ] && [ "$ARETVAL" = "0" ]; then
COPYDB="1"
fi
if [ "$COPYNEWDB" = "yes" ]; then
COPYDB=1
fi
if [ "$COPYDB" = "1" ] && [ "$COMMAND" = "update" ]; then
cp -f "$DATABASE_OUT" "$DATABASE"
printf >> "$POSTRUNLOG" "no significant changes detected.\n"
printf >> "$POSTRUNLOG" "output database %s was copied to %s as requested by cron job configuration\n" "$DATABASE_OUT" "$DATABASE"
fi
onexit success
# end of file
More information about the Pkg-aide-maintainers
mailing list