So, forever and a day I’ve been using a service at autorespond+dkim@dk.elandsys.com to provide a simple autoreply for testing connectivity to a mail server. Sadly, the service has been discontinued after many years and so I decided to set up my own with Postfix, the most venerable of mail servers. Here’s how I did that.
Assumptions
-
You’re using a fairly recent edition of Postfix. I’m using 3.7.10. You can find out your version using
postconf -d mail_version. -
You’re using virtual mailboxes, meaning you have a file similar to
/etc/postfix/virtual(and.db) that maps email addresses to local user mailboxes. You can check this by looking in/etc/postfix/main.cffor a line likevirtual_alias_maps = hash:/etc/postfix/virtualandvirtual_alias_domains = yourdomain.com. -
You’ve backed up your Postfix configuration files using the command
cd /etc/postfix && tar -czvf /root/postfix-backup-$(date +%d.%m.%Y).tar.gz .. -
You’ve checked the backup with
tar -tf /root/postfix-backup-$(date +%d.%m.%Y).tar.gz
Everything else we’ll set up so let’s get started.
Step 1: Create a new user called filter
This will create the user called filter with a home directory of /var/spool/filter and a shell of /bin/false. This user will be used to run the autoreply scripts safely.
sudo useradd -d /var/spool/filter -m -s /bin/false filter
Step 2: Add the new user’s mailbox entry to the virtual file
Add the autoreply line autoreply@yourdomain.com filter to your /etc/postfix/virtual file, so it looks something like this..
# compile with postmap /etc/postfix/virtual
hello@yourdomain.com simon
morgan@yourdomain.com morgan
james@yourdomain.com james
simon@yourdomain.com simon
sandra@yourdomain.com sandra
recruitment@yourdomain.com simon sandra
salesteam@yourdomain.com james morgan sandra reporting@parentco.co.uk
autoreply@yourdomain.com filter
This means that email to autoreply@yourdomain.com will be delivered to the filter user’s mailbox.
Save the file and then compile it with postmap /etc/postfix/virtual.
Step 3: Update the Postfix configuration, adding a new service
The /etc/postfix/master.cf file controls how Postfix runs daemons for mail collection, delivery and so on. We’re going to add a new service that will be called every time a mail is received. Add the following to the end of the file:
filter unix - n n - 10 pipe
flags=Rq user=filter null_sender=
argv=/var/spool/filter/mailfilter.sh -f ${sender} -- ${recipient}
In plain English, this says “Set up a service called filter that is..
- a unix process
- is private (- = ‘use the default setting,
yes’) - is unprivileged (n = ‘use no. Default is
yes’) - is chrooted (n = ‘use no. Default is
yes’) - wakeup (- = ‘use the default setting,
never’) - 10 (max number of processes)
- pipe (use a pipe to communicate with the script or software which we will call)
On the next line, the most important parts are:
user=filter(run the script as thefilteruser). Note that the service and user names are the same, but they are not somehow dependant. They are unrelated.argv=/var/spool/filter/mailfilter.sh -f ${sender} -- ${recipient}(run the script/var/spool/filter/mailfilter.shwhich will be passed four arguments:-f, the sender,--and the recipient). Seems odd, but this is from the postfix documentation. We’ll be using arguments 2 and 4 in our script for the sender and recipient.
Step 4: Update the Postfix configuration, adding the filter service to the smtpd service
To turn on the content filtering service for smtp that we configured in step 3, add the following to the /etc/postfix/master.cf file:
Look down the left hand side of the file for the smtp service and add the -o content_filter=filter:dummy line just below it, as shown below:
smtp inet ...other stuff here, do not change... smtpd
-o content_filter=filter:dummy
Mine looks like this because I’m also using Amavis for spam filtering:
smtp inet n - y - 20 smtpd
-o content_filter=smtp-amavis:[127.0.0.1]:10024
-o smtpd_sasl_auth_enable=no
-o content_filter=filter:dummy
You can save master.cf. We’re done with that file.
Step 5: Create the mailfilter.sh script
Create the file /var/spool/filter/mailfilter.sh with the following contents. Download from Github Gists.
#!/bin/sh
# Simple shell-based filter. It is meant to be invoked as follows:
# /path/to/script -f sender recipients...
# Localize these. The -G option does nothing before Postfix 2.3.
INSPECT_DIR=/var/spool/filter
SENDMAIL="/usr/sbin/sendmail -G -i" # NEVER NEVER NEVER use "-t" here.
# Exit codes from <sysexits.h>
EX_TEMPFAIL=75
EX_UNAVAILABLE=69
# Clean up when done or when aborting.
trap "rm -f in.$$" 0 1 2 3 15
# Start processing.
cd $INSPECT_DIR || {
echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; }
cat >in.$$ || {
echo Cannot save mail to file; exit $EX_TEMPFAIL; }
# Specify your content filter here.
/var/spool/filter/process-mail.sh "$@" <in.$$ || {
echo Message content rejected; exit $EX_UNAVAILABLE; }
$SENDMAIL "$@" <in.$$ || {
echo Unable to send mail; exit $EX_UNAVAILABLE;
}
exit $?
This file does not need any modification. It will pass control to the process-mail.sh script which we will create next. This script will do all the work.
Step 6: Create the process-mail.sh script
Create the file /var/spool/filter/process-mail.sh with the following contents, adjusting SENDMAIL and DOMAIN values as needed. LOCAL_AUTOREPLY_ADDRESS should be fine. Download from Github Gists.
#!/bin/bash
# $2 is the email sender. We want to autoreply to them
# $4 is our filter user email address, typically filter@localhost
# Check the parameters below are right for your system
SENDMAIL="/usr/lib/sendmail"
DOMAIN="YOUR-DOMAIN-NAME-HERE.com"
LOCAL_AUTOREPLY_ADDRESS="filter@localhost"
if [ "$4" = $LOCAL_AUTOREPLY_ADDRESS ]; then
echo "OK, I'll send a reply to |$2|"
printf '%s\n' "Subject: Autoreply from $DOMAIN" \
"From: Autoreply <autoreply@$DOMAIN>" \
"To: Recipient <$2>" \
"" \
"--------------------------------------------------------------------------------" \
" " \
" This is the autoresponder for $DOMAIN. Your mail was received!" \
" " \
"--------------------------------------------------------------------------------" | $SENDMAIL -oi -t
else
echo "Not an autoreply address."
fi
Step 7: Set privs and ownership
Make the scripts executable and owned by the filter user:
sudo chown filter:filter /var/spool/filter/mailfilter.sh /var/spool/filter/process-mail.sh
sudo chmod +x /var/spool/filter/mailfilter.sh /var/spool/filter/process-mail.sh
Step 8: Restart Postfix
Restart Postfix to pick up the changes:
sudo systemctl restart postfix
Step 9: Test
Start by tailing the mail log, but only looking for the appearance of the pipe service we added to master.cf. postfix is a bit chatty.
tail -f /var/log/mail.log | grep pipe
OK, now send a test email to yourself e.g. morgan@yourdomain.com from outside to get something like this..
2024-05-03T20:02:49.693321+02:00 mail postfix/pipe[403102]: 7D5342001BB: to=<morgan@localhost>, orig_to=<morgan@yourdomain.com>, relay=filter, delay=0.24, delays=0.22/0/0/0.02, dsn=2.0.0, status=sent (delivered via filter service (Not an autoreply address.))
As you can see, it’s been through the filter and the process-mail.sh script has decided it’s not an autoreply address.
Now send an email to autoreply@yourdomain.com and you should see something like this:
2024-05-03T20:03:07.066805+02:00 mail postfix/pipe[403102]: EC7882001BB: to=<filter@localhost>, orig_to=<autoreply@yourdomain.com>, relay=filter, delay=0.2, delays=0.15/0/0/0.05, dsn=2.0.0, status=sent (delivered via filter service (OK, I'll send a reply to |morgans.testmails@outlook.com|))
The end user should get something like this..
Subject: Autoreply from yourdomain.com
From: Autoreply <autoreply@yourdomain.com>
To: Recipient <morgans.testmails@outlook.com>
Message-Id: <20240503180307.091F72003BC@mail.yourdomain.com>
Date: Fri,
3 May 2024 20:03:07 +0200 (CEST)
--------------------------------------------------------------------------------
This is the autoresponder for yourdomain.com. Your mail was received!
--------------------------------------------------------------------------------
So there you have it. A simple autoreply service for your domain using Postfix. You can now customise the process-mail.sh script to do whatever you like with the incoming mail, such as incrementing a counter on Slack or website or maybe writing it to an .rrd file for graphing or something.
You can test all this by emailing autoreply@cloudguyinbroadstone.com and it will reply to you.