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.
[code]
#!/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
[/code]