#!/bin/sh - #### set MAILFROM per site #### MAILFROM=postmaster IGNORE='(: hold: |: replace: |: filter: |broken.example.com|greylist| warning|DISCARD|unverified.address: Address verification in progress|Operation timed out)' SECTIONMAX=5000 LOGS='/var/log/mail* /var/log/messages*' OLDLOGS='' DEBUG= #[Y/y/else N] (to display instead of email, see also -d flag) #### redefine $host only if localhost is not the mailhost host=`uname -n|awk -F. '{print $1}'` #### use: MAILCMD="sendmail -oem -oi -t -f $MAILFROM" with sendmail >V8.8 #### "-t" might work with Postfix versions 20030611 and newer MAILCMD="sendmail -oem -oi -f $MAILFROM" #### customizable end of report SIGNATURE="www.postconf.com/docs/spamrep" ###################################################################### # spamrep_yesterday_bydom spamrep_today_bydom (Postfix version) # # # # License terms at: # HTML version available with PostConf ###################################################################### PATH=/usr/ucb:/bin:/usr/bin:/usr/sbin:/sbin:/usr/lib _POSIX2_VERSION=199209 LANG=C LESS=-ce TIMEZONE="`date | awk '{print $5}'`" TMP="/tmp/.spamrepd.$$" umask 077 set -a trap "rm -f $TMP $TMP.spam $TMP.sum $TMP.today ; exit 1" 0 1 2 3 15 #### test minlines whenever changing the report #### MINLINES=9 if [ $# -eq 0 ] || [ "$1" = "-help" ] || [ "$1" = "-h" ] || [ "$1" = "-H" ]; then echo " USAGE: `basename $0` [-d|-h] \*@domain " exit elif [ "$1" = "-d" ] || [ "$1" = "-D" ]; then DEBUG=Y REPORTON=$2 MAILTO=nobody shift elif [ $# = 1 ]; then DEBUG=Y REPORTON=$1 MAILTO=nobody else REPORTON=$1 MAILTO=$2 fi if [ "`date '+%d' | sed 's/0*//'`" = 1 ]; then LOGS="${LOGS} ${OLDLOGS}" fi if [ "`basename $0 | grep -i yesterday`" != "" ]; then #### #### pgmname = *yesterday* ... #### if [ "`date '+%d' | sed 's/0*//'`" = 1 ]; then #### unreliable hack for 1st of the month: TZ="`date| awk '{print $5}'`+31" LOGDATE="`date|awk '{printf "%3s%3s", $2, $3}'`" DAY="`date|awk '{print $2, $3, $NF}'`" else LOGDATE="`date|awk '{printf "%3s%3s", $2, $3-1}'`" DAY="`date|awk '{print $2, $3-1, $NF}'`" fi else #### #### pgmname = *today* ... #### LOGDATE="`date|awk '{printf "%3s%3s", $2, $3}'`" DAY="`date|awk '{print $1, $2, $3, $NF}'`" fi printSecHead () { SS="`wc -l $TMP.spam 2>/dev/null | awk '{print $1}'`" echo "" >> $TMP if [ $SS -eq 1 ]; then barheader "$SS $sectionTitle1" >> $TMP echo " $SS $sectionTitle1" >> $TMP.sum elif [ $SS -gt $SECTIONMAX ]; then barheader "displaying $SECTIONMAX of $SS $sectionTitle2" >> $TMP echo " displaying $SECTIONMAX of $SS $sectionTitle2" >> $TMP.sum else barheader "$SS $sectionTitle2" >> $TMP echo " $SS $sectionTitle2" >> $TMP.sum fi } printSecFoot () { echo "" >> $TMP head -${SECTIONMAX} $TMP.spam >> $TMP rm -f $TMP.spam } printRBLdetail () { RBLS=`grep blocked.using $TMP.spam | awk -F\; '{ print $2 }' | awk '{ print $NF }' | sort -u` #RBLS=`sed -e 's/^.*blocked using //' -e 's/, reason.*$//' -e 's/;.*$//' $TMP.spam | sort -u` if [ "`echo $RBLS | wc -w`" -gt 1 ]; then rblCalc () { for rbl in $RBLS ; do rblhits="`grep $rbl $TMP.spam 2>/dev/null | wc -l 2>/dev/null`" if [ $rblhits -ge 1 ]; then echo " $rblhits - $rbl (`expr 100 \* $rblhits / $SS`%)" fi done } rblCalc | sort -rn >> $TMP.sum fi } barheader () { echo "------[ $1 ]----------------------------------------------------------------" | \ awk -F"\n" '{ printf "%-.75s", $1 }' 2>/dev/null } report_on_dom () { #################### email headers ################################### rm -f $TMP.spam $TMP.sum $TMP #### "To: ${MAILTO}" is required but may be ignored if recipient is #### specified on the command line. echo "From: ${MAILFROM} To: ${MAILTO} Reply-To: ${MAILFROM} Subject: $DAY mailstats for $REPORTON " > $TMP.sum #################### report headers ################################## echo "" >> $TMP echo " Reply to this email or forward to $MAILFROM if" >> $TMP echo " you find any false-positives or have questions about" >> $TMP echo " spam filtering." >> $TMP echo " " >> $TMP echo " Important reminder: please do not reply to unsolicited " >> $TMP echo " email." >> $TMP # echo "" >> $TMP.sum barheader "$DAY mailstats for $REPORTON" >> $TMP.sum echo "" >> $TMP.sum echo "" >> $TMP.sum #################### list by filter type ############################# grep -v " reject_warning: " $TMP.today | grep 'from=<' | \ sed -e 's/^.* from= to=<.*$/>/' | sort -t\< +1f -u \ > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="unique sender filtered:" sectionTitle2="unique senders filtered:" printSecHead printSecFoot fi grep -i "blocked.using" $TMP.today | grep -v " reject_warning: " | \ sed -e 's/ Service unavailable//' \ -e 's/Barracuda Reputation, see/b.barracudacentral.org;/' \ -e 's/ Client host .* blocked using / blocked using /' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="rejected by subscription" sectionTitle2="$sectionTitle1" printSecHead printRBLdetail printSecFoot fi egrep -i '(reject: header|discard:|message.size.exceeds|access.denied|Message.content.rejected)' $TMP.today | \ egrep -iv '(user.unknown|discard: header X-Amavis.*: INFECTED|discard: header X-Spam| reject_warning: |Recipient.address.rejected|helo.command.rejected|domain.not.found|need.fully-qualified|Relay access denied)' | \ sed 's/: Access denied//' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="rejected by localhost" sectionTitle2="$sectionTitle1" printSecHead printSecFoot fi egrep -i '(no such user|user.unknown)' $TMP.today | egrep -v '( reject_warning: )' | \ sed 's/ in local recipient table//' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="user unknown" sectionTitle2="$sectionTitle1"s printSecHead printSecFoot fi grep 'Relay access denied' $TMP.today > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="relay attempt" sectionTitle2="$sectionTitle1"s printSecHead printSecFoot fi egrep -i '(helo.command.rejected|domain.not.found|need.fully-qualified)' $TMP.today | \ egrep -iv '( reject_warning: |reject: header|discard:|message.size.exceeds|access.denied|Recipient.address.rejected|Message.content.rejected)' | \ sed 's/: Access denied//' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="protocol error" sectionTitle2="$sectionTitle1"s printSecHead printSecFoot fi grep -i "discard: header X-Spam" $TMP.today | \ sed -e 's/ proto=ESMTP helo=.localhost.//' -e 's/from localhost.*127.0.0.1.; //' \ -e 's/ from local;//' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="discarded by spamassassin" sectionTitle2="$sectionTitle1" printSecHead printSecFoot fi #### assumes clam-av, amavis #### egrep '(discarded, .* - VIRUS|discard: header X-Amavis.*: INFECTED)' $TMP.today | \ sed -e 's/'$host' .*: to/'$TIMEZONE' virus: to/' \ -e 's/ proto=ESMTP helo=.*$//' \ -e 's/ from localhos.*\[127.0.0.1\]//' \ -e 's/, relay=127.0.0.1\[127.0.0.1\].*status=sent//' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="discarded by anti-virus" sectionTitle2="$sectionTitle1" printSecHead printSecFoot fi egrep -i '( warning: | error: | fatal: | panic: )' $TMP.today | \ egrep -v '( reject_warning: |older than source)' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="notice" sectionTitle2="$sectionTitle1"s printSecHead printSecFoot fi grep " reject_warning: " $TMP.today | \ sed -e 's/blocked using/listed by/' \ -e 's/reject_warning: /warn_only: /' \ -e 's/ ... Service unavailable; //' > $TMP.spam if [ -s $TMP.spam ]; then sectionTitle1="warn-only notice" sectionTitle2="$sectionTitle1"s printSecHead printSecFoot fi #################### finish header and email ################## echo "" >> $TMP if [ "`wc -l $TMP | awk '{print $1 }'`" -lt $MINLINES ]; then rm -f $TMP.spam $TMP.sum $TMP $TMP.today exit else cat $TMP >> $TMP.sum barheader "end of report - $SIGNATURE" >> $TMP.sum echo "" >> $TMP.sum rm -f $TMP $TMP.spam fi if [ "$DEBUG" = y ] || [ "$DEBUG" = Y ]; then more $TMP.sum else $MAILCMD $MAILTO < $TMP.sum fi rm -f $TMP $TMP.spam $TMP.sum $TMP.today } #################### run the reports ################################# ######### may need to customize sed per local syslog format ########## grep -h "^$LOGDATE" $LOGS 2>/dev/null | grep postfix | egrep -iv "$IGNORE" | \ grep -i " to=.${REPORTON}" | grep -v 'postfix/cleanup.*relay=none' | sort -Mr | \ sed -e 's/ NOQUEUE://' \ -e 's/ proto=ESMTP / /' -e 's/ proto=SMTP / /' \ -e 's/'"$host"' postfix.* reject:/'$TIMEZONE' reject:/' \ -e 's/'"$host"' postfix.* discard:/'$TIMEZONE' discard:/' \ -e 's/'"$host"' postfix.* error:/'$TIMEZONE' error:/' \ -e 's/'"$host"' postfix.* fatal:/'$TIMEZONE' fatal:/' \ -e 's/'"$host"' postfix.* panic:/'$TIMEZONE' panic:/' \ -e 's/'"$host"' postfix.* reject_warning:/'$TIMEZONE' reject_warning:/' \ -e 's/'"$host"' postfix.* warning:/'$TIMEZONE' warning:/' \ -e 's/'"$host"' postfix.*smtp.*mail.info]/'$TIMEZONE'/' > $TMP.today report_on_dom ################### cleanup ########################################## rm -f $TMP.spam $TMP.sum $TMP $TMP.today