Check number of outgoing mail through ASSP

Last week we had a spam issue when a user’s mail credentials were apparently guessed or stolen. (Our mail server doesn’t do two factor yet.) A lot of spam was sent from that useraccount.

To monitor our relay server I wrote a script that checks for unusual amounts of mail being sent. It sends a notification when it spots higher outgoing traffic than usual on a user’s e-mail account.

The script works in conjunction with ASSP’s mail archive plugin.

#!/bin/bash

#############################################################
# mailvolumealert.sh - Martin Huijgen, 2017-07-07           #
# Published on Vorkbaard.nl                                 #
#############################################################
# This script is supposed to work in conjunction with       #
# ASSP's archive plugin. It bases outgoing mail volume on   #
# ASSP's mail archive folder.                               #
#                                                           #
# It counts how many mail a user has sent today. If it is   #
# significantly more than the last few days, alert the      #
# admin.                                                    #
#                                                           #
# Written for on Ubuntu 17.04.                              #
#############################################################


#############################################################
# Parameters                                                #
#############################################################

ARCHIVEDIR="/var/assp/archive/outgoing" # Where the mail archive is stored

AVGDAYS=10 # How many working days to use for average (Saturdays and Sundays are excluded)
ALWAYS_ALLOW=100 # Allow this many mails to be sent without notification
PERC_ALLOWED=150 # Percentage of mail the user is allowed to send relative to the average
UPPER_LIMIT=200 # Always notify if a user sent more mail than this

ADMIN_EMAIL=your@email.here # Where to send alerts
FROM_ADDR=$ADMIN_EMAIL # Which address should we say is the mail from?

# File to remember which users have already been reported today. Only change this
# if you really need it.
PREV_REPORT_FILE=mailnotified.$(date -d "1 day ago" +%A)
CUR_REPORT_FILE=mailnotified.$(date +%A)

# Remember to check your 'mailx' or 'xmail' settings on the last
# few lines of this script.

#############################################################
# End of parameters                                         #
#############################################################

# A bit of preparation

# Check if the archive folder exists
if [ ! -d $ARCHIVEDIR ] ; then
        echo "$ARCHIVEDIR does not exist, please correct the ARCHIVEDIR variable."
        exit
fi

# Check if there is any mail to count
TODAYS_MAIL_FLD=$ARCHIVEDIR/$(date +%Y)/$(date +%m)/$(date +%d)

# If noone has send any mail today then don't bother.
if [ ! -d $TODAYS_MAIL_FLD ] ; then exit; fi

CUR_FLD=$(ls -d $TODAYS_MAIL_FLD)
SENDERS=$CUR_FLD/*
SENDERNR=0
SEND_ALERTMAIL=false
ALERTMAIL="The following users are sending an unusual amount of mail or have breached the upper notification limit ($UPPER_LIMIT mails).\n"

# Delete yesterday's report file and create the new one if it doesn't exist
if [ -e $PREV_REPORT_FILE ] ; then rm $PREV_REPORT_FILE ; fi
touch $CUR_REPORT_FILE

# Here we go!

# Go to mail archive folder; enumerate users
for SENDER in $SENDERS; do
  SENDER=$(echo $SENDER | rev | cut -d'/' -f1 | rev)

        # If we were already notified about this sender, skip them
        if ! ( grep -iq "$SENDER" $CUR_REPORT_FILE ) ; then

          # Calculate average amount of mail based on last ten working days
          DATE_FORMAT="+%Y%m%d"
          PREVDAYS=1
          WEEKDAYS=0
          while [ $WEEKDAYS -lt $AVGDAYS ] ; do
            DAYAGO=$(date "$DATE_FORMAT" -d "$PREVDAYS day ago") # Substract $PREVDAYS days from today
            DAYNUMBER=$(date -d "$DAYAGO" +%u) # Get day number; 1 is Monday
                        NR_OF_DAYS=0
                        TOT_MAIL=0
            if [ "$DAYNUMBER" -lt "6" ] ; then

              # Get number of mails for this user on this day
                                CURDIR=$ARCHIVEDIR/${DAYAGO:0:4}/${DAYAGO:4:2}/${DAYAGO:6:2} # Check if the folder exists
                                if [ -d "$CURDIR/$SENDER" ] ; then
                                        FLD_CNT[$WEEKDAYS]=$(ls $CURDIR/$SENDER/* | wc -l)
                                fi

                                NR_OF_DAYS=${#FLD_CNT[@]}
                                TOT_MAIL=0
                                for i in ${FLD_CNT[@]}; do let TOT_MAIL+=$i; done # Calculate total number of mails

              WEEKDAYS=$(( $WEEKDAYS + 1 ))
            fi

           PREVDAYS=$(( $PREVDAYS + 1 ))

                 done #WEEKDAYS -lt 11

                # If the counting yielded any results, use them
                ALLOWEDMAIL=0
                if [ "$NR_OF_DAYS" -gt "0" ] ; then
                        let AVG_MAIL=$TOT_MAIL/$NR_OF_DAYS
                        # echo "$SENDER Average number of mails per day over the last $AVGDAYS workdays: $AVG_MAIL"
                        let ALLOWEDMAIL=($AVG_MAIL*$PERC_ALLOWED)/100 # The amount of mail allowed to be sent
                        if [ "$ALLOWEDMAIL" -lt "$ALWAYS_ALLOW" ] ; then ALLOWEDMAIL=$ALWAYS_ALLOW; fi # Maintain the lower threshold

                else
                        # Perhaps this is a new user. Set their average to the lower threshold.
                        ALLOWEDMAIL=$ALWAYS_ALLOW
                fi

                # Find how many mails this user has sent today
                TODAYSMAIL=$(ls $ARCHIVEDIR/$(date +%Y)/$(date +%m)/$(date +%d)/$SENDER/* | wc -l)

                  # This is for troubleshooting
                  # echo "$SENDER - avg: $AVG_MAIL; allowed: $ALLOWEDMAIL"


                # If the user sent more mail than allowed, send a notification
          if [ "$TODAYSMAIL" -gt "$ALLOWEDMAIL" ] || [ "$TODAYSMAIL" -gt "$UPPER_LIMIT" ] ; then
                        SEND_ALERTMAIL=true
                        ALERTMAIL="$ALERTMAIL\n$SENDER - 10 day average is $AVG_MAIL, today's amount $TODAYSMAIL."
                        # Remind next script run to not scan this user again
                        echo "$SENDER" >> $CUR_REPORT_FILE
          fi

        fi # grep sender in cur_report_file

done #SENDER IN SENDERS

# Send the alertmail if necessary
if ($SEND_ALERTMAIL); then
        echo -e $ALERTMAIL | mailx -a "From: Mail relay server <$FROM_ADDR>" -s "Mail warning" $ADMIN_EMAIL
fi

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back to Top