When a large scale spam run was sent through your mail servers, you need to clean up and remove those spam messages. Doing so guarantees normal, valid email messages being sent quickly, and the spam messages never leave your queue.
In this post I show you how you can delete email from the mail queue in Postfix, based on a condition. For example all MAILER-DAEMON emails or spam, by sender (From) or recipient (To) address or Message-ID header.
How to delete all emails from a Postfix queue when it matches an address condition
I use this regularly to remove spam from my Postfix SMTP server mail queue. These oneliners are nice to have in Bash.
If you want to delete emails from your Postfix queue based on a sender address - spammer@example.com for example - you need to feed the mailq
output to awk
. The result is fed to postsuper
for deletion:
mailq | tail -n +2 | awk 'BEGIN { RS = "" }
{ if ( $7 == "spammer@example.com" ) print $1 }
' | tr -d '*!' | postsuper -d -
mailq | tail -n +2 | awk 'BEGIN { RS = "" }
{ if ( $7 ~ "@example.com" ) print $1 }
' | tr -d '*!' | postsuper -d -
Here, $7
represents the sender address (7th awk field), either full (spammer@example.com) or matched by regular expression (~ @example.com
).
Do you want to delete message sent to a particular address? Use $8
or $9
as they represent recipient addresses:
mailq | tail -n +2 | awk 'BEGIN { RS = "" }
{ if ( $8 == "spammer@example.com" ) print $1 }
' | tr -d '*!' | postsuper -d -
mailq | tail -n +2 | awk 'BEGIN { RS = "" }
{ if ( $8 == "spammer@example.com" && $9 == "" ) print $1 }
' | tr -d '*!' | postsuper -d -
mailq | tail -n +2 | awk 'BEGIN { RS = "" }
{ if ( $8 == "spammer@example.com" || $8 == "spammer2@example2.com" ) print $1 }
' | tr -d '*!' | postsuper -d -
and so on.
But what if those spam mails already left your system? And can't be delivered? Then you're stuck with hundreds of thousands MAILER-DAEMON messages. Of course you can delete all those mails from your Postfix queue: just use MAILER-DAEMON as sender address in the above scripts.
But doing so, you probably also delete other, possibly valid MAILER-DAEMON mails informing you about undeliverable emails. Therefore you need to build in a condition.
The following shell script scans through the Postfix mail queue - using the mailq
command - looking for messages sent by MAILER-DAEMON, or any other email address you provide as argument. Of those message-id's it scans to see if the recipient condition is matched.
Those messages are then deleted from the queue by executing postsuper -d -
.
The following example script takes the recipient email address as input, and deletes all mails where MAILER-DAEMON is the sender:
#!/bin/bash
EMAILADDY=$1
if [ -z "$EMAILADDY" ]
then
echo "Usage: $0 <email adres>"
exit
fi
echo "Delete all emails addressed to $EMAILADDY, and sent by MAILER-DAEMON, from our Postfix queue."
mailq | tail -n +2 | grep -v '^ *(' | awk -v "address=$EMAILADDY" 'BEGIN { RS = "" }
{
# example conditions:
# if ( $7 == "MAILER-DAEMON && $8 == address && $9 == "" )
# if ( $7 == "MAILER-DAEMON && $8 == "" && $9 == address )
# if ( $7 == "MAILER-DAEMON && $8 == address || $9 == address )
if ( $7 == "MAILER-DAEMON && $8 == address )
print $1
}
' | tr -d '*!' | postsuper -d -
Call your script on the command line providing a recipient email address:
sh ./delete-mail-from-queue.sh spammer@example.com
Postfix, delete all mails from the queue queue sent by spammer@example.com:
#!/bin/bash
EMAILADDY=$1
if [ -z "$EMAILADDY" ]
then
echo "Usage: $0 <email address>"
exit
fi
echo "Delete all emails addressed to $EMAILADDY from our Postfix queue."
mailq | tail -n +2 | grep -v '^ *(' | awk -v "address=$EMAILADDY" 'BEGIN { RS = "" }
{
# example conditions:
# if ( $8 == address && $9 == "" )
# if ( $8 == address || $9 == address )
if ( $8 == address )
print $1
}
' | tr -d '*!' | postsuper -d -
Call your script on the command line providing an email address:
sh ./delete-mail-from-queue.sh spammer@example.com
To use a regular expression in Awk, suppose you want to match all emails from various @example.com addresses, change the line if ($8 == address)
to:
# the sender is the 7th Awk field, thus $7
if ($7 ~ address)
and provide only example.com as a parameter:
sh ./delete-mail-from-queue.sh example.com
Again, use $8
and/or $9
to match recipients, assuming MAILER-DAEMON is the sender / $7
.
Remove mail from multiple Postfix instances queues
When you are using multiple Postfix instances, and control them with postmulti, then you can use command line arguments in your script. For example:
#!/bin/bash
QUEUETYPE=$1
EMAILADDY=$2
if [ -z "$QUEUETYPE" ] || [ -z "$EMAILADDY" ]
then
echo "Usage: $0 <queuetype web|smtp|incoming> <email address>"
exit
fi
echo "Delete all emails addressed to $EMAILADDY from our Postfix $QUEUETYPE queue."
postmulti -i $QUEUETYPE -x mailq | tail -n +2 | grep -v '^ *(' | awk -v "address=$EMAILADDY" 'BEGIN { RS = "" }
{
if ( $7 == address )
print $1
}
' | tr -d '*!' | postmulti -i $QUEUETYPE -x postsuper -d -
Example usage is: sudo ./clean-mailqueue incoming MAILER-DAEMON
.
Always be careful when messing with mail queues, and especially regular expressions.