Nethence Newdoc Olddoc Lab Your IP BBDock  

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 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/ ./
cp -pi /usr/share/postfix/ ./
cp -pi
#diff /usr/share/postfix/ 

and edit the configurations based on those examples,



Note that the Docker network is added to mynetworks so e.g. the RBL checks won’t be performed against the MX server itself, in case is it Dockerized.

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,   REJECT is identified as a spam domain  REJECT 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),       OK              OK                 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 25
helo my.real.resolvable.remote.fqdn
mail from:<some.real@email>
rcpt to:<>

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.


GIT vs script

Eventually maintain the diffs if not using GIT,

    cd ~/
    chmod +x confcheck.bash

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   REJECT is identified as a spam domain  REJECT 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       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.


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

Backup / Secondary MX

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


References about anti-spam & RFC compliance

References for MariaDB mappings

Home | GitHub | Donate | Contact