Hardened anti-spam MX with Postfix


In this guide, the users' Maildir folders are possibly stored on a shared volume (NFS or shared disk fs), however the user db is still on the server itself into /etc/passwd. The best would be to switch mappings to MariaDB or something else for the running node to be independent from the data. Same goes if you are proceeding as a Docker container: it provides the application and is ideally data-independent.


Make sure your system up-to-date,

apt update
apt -y full-upgrade
apt autoremove
dpkg -l | grep ^rc

Make sure you’ve got those installed,

export DEBIAN_FRONTEND=noninteractive
apt -y install \
    postfix bsd-mailx rsyslog \
#mailutils pmailq

The Postfix config syntax changes depending on the version. I am using 3.1.0 (and most recently 3.3.0) here. Check with,

postconf -d|grep ^mail_ver

Postfix prep

Make some handy symlinks for operations (assuming this is a dedicated node or container),

cd ~/
ln -s ../home
ln -s ../etc/postfix
ln -s /etc/aliases
ln -s /var/log/mail.log
ln -s /var/log/mail.err

Copy or backup the default confs,

cd /etc/postfix/
cp -pi /usr/share/postfix/main.cf.debian ./
cp -pi /usr/share/postfix/main.cf.dist ./

clean those up

sed '/^#/d; /^$/d' master.cf.dist > master.cf
sed '/^#/d; /^$/d' main.cf.dist > main.cf

mv -i master.cf master.cf.dist
#diff master.cf.dist /usr/share/postfix/master.cf.dist 

get my examples

wget -q http://doc.nethence.com/download/postfix/main.cf.txt
wget -q http://doc.nethence.com/download/postfix/master.cf.txt

and edit the settings based on those. Once you are done, eventually proceed as such to keep up with the jam

cd /etc/postfix/
diff -bu main.cf.txt main.cf
diff -bu master.cf.txt master.cf

Note that myorigin is set to mydomain so you should tweak the /etc/passwd file and add some information about the user so you can more easyly determine where the automatically generated messages are coming from,



The client_access file may look like this,

compute.amazonaws.com   REJECT compute.amazonaws.com is identified as a spam domain
.compute.amazonaws.com  REJECT compute.amazonaws.com is identified as a spam domain

postmap client_access

The helo.regexp file may look like this,

/^mx\.nethence\.com$/          550 you are not me
/^xc\.nethence\.com$/          550 you are not me
/^nethence\.com$/               550 you are not me

The sender_access file may look like (this is optional as it goes togeather with the unverified sender policy feature that is not enabled),

securityfocus.com       OK
online.net              OK
ovh.com                 OK

postmap sender_access

Allow Postfix to resolve hosts from its chroot land,

cd /var/spool/postfix/etc/
cp -pf /etc/resolv.conf /etc/hosts /etc/services ./

mkdir -p /var/spool/postfix/lib/x86_64-linux-gnu/
cd /var/spool/postfix/lib/x86_64-linux-gnu/
cp -vl /lib/x86_64-linux-gnu/libnss_* ./
ls -alhF
cd ~/

Make sure the mail users don’t get the dotfile skeletons,

mv -i /etc/skel/ /etc/skel.dist/
mkdir /etc/skel/

Don’t forget to tweak the system’s or container’s aliases e.g.,

cd /etc/
cp -pi aliases aliases.dist
vi aliases

root: real_mailbox_user
wheeleduser: root
abuse: root
contact: root
info: root
sales: root
hostmaster: root
www: root
webmaster: root
postmaster: root



Setup the MX record and SPF.

Ready to go

Create a mailbox (see Ops chapter below) for first-level acceptance.

If you stick to the provided init scripts (not a container), restart the thing,

service rsyslog restart
service postfix restart

otherwise (e.g. for a container),

postfix start

Make sure the unix socker for SPF is up and running,

netstat -an|grep policy

Read the logs,

cd ~/logs.bash

tail -n 25 -F /var/log/mail.*

chmod +x logs.bash

while doing some testing from remote hosts (try with an authorized IP as well as from a should-be-blocked IP e.g. ADSL connection),

telnet xc.nethence.com 25
helo my.real.resolvable.remote.fqdn
mail from:<some.real@email>
rcpt to:<user@nethence.com>

and also try to send an email to yourself with an email client of course (is your smarthost is in the SPF?). And don’t forget to do a last telnet check against an SMTP open proxy service, just in case you messed up smtpd_relay_restrictions.

Don’t forget to enable Fail2ban to protect yourself from bot nets.


See the fellow Dovecot guide.


Applying minor Postfix configuration changes,

Within or outside a container,

postfix reload

Creating / Removing Mailboxes

Create a new mailbox,

#useradd -g users -m -k /dev/null -s /sbin/nologin $new
useradd -g users -m -k /dev/null -s /dev/null $new
passwd $new
unset new

Note. The Maildir/ folder into the user’s homedir will be created by Postfix when the first mail arrives.

Remove a mailbox and loose its data,

userdel -r <someuser>


Deal with spam that eventually came through the hereby protections (wow, so that’s a clean spam!): look at the headers what fqdn connected to your MX and reject it manually so it doesn’t spam you again,

vi /etc/postfix/client_access

compute.amazonaws.com   REJECT compute.amazonaws.com is identified as a spam domain
.compute.amazonaws.com  REJECT compute.amazonaws.com is identified as a spam domain

postmap /etc/postfix/client_access

(Optional) False-positives

The unverified sender feature is not enabled. In case you do enable it, you need to deal with false-positives on the domains that do not pass tru the brutal verify-sender-against-all setting,

vi /etc/postfix/client_access

securityfocus.com       OK

postmap /etc/postfix/sender_access


If you get this error while starting Postfix,

postfix/postfix-script: warning: group or other writable: /usr/lib/postfix/./sbin/lmtp

==> don’t give a fuck about it, it’s complaining about symlinks' rights which point to files with OKAY permissions.

If you get this error quite often in the logs,

close database /var/lib/postfix/verify_cache.db: No such file or directory (possible Berkeley DB bug)

==> use proxy: in the address_verify_map statement as shown above.

Ref. http://www.postfix.org/ADDRESS_VERIFICATION_README.html

If you need to unblock your server from MS’s blacklists:

Backup / Secondary MX

Play with relay_domains, transport_maps and do NOT list example.com into mydestination.

Dealing with Outlook.com

First, register to live.com and define your IP into SNDS: https://postmaster.live.com/snds/

Second, file a request to un-block your IP: https://support.microsoft.com/en-us/getsupport?oaspworkflow=start_1.0.0.0&wfname=capsub&productkey=edfsmsbl3


References about anti-spam & RFC compliance

References for MariaDB mappings

Nethence | Doc | Pub | Lab | Pbraun | SNE Russia | xhtml