Installing MailScanner 5.3 with Postfix on Ubuntu 20.04 LTS

This is an installation guide for installing MailScanner 5.3 with Postfix on Ubuntu 20.04 LTS. The situation we want to create is first have an incoming mail scanned by MailScanner and after it is found ok to be delivered to your Exchange or Zimbra mail server.

This document is a step by step guide, so you might do some steps differently because you prefer other options or packages.

This document is not finished yet

TTL of your Domains

In the DNS servers of your domain set it to something very short: 15 minutes or so

Update your server

I created a fresh VM for the purpose of writing this how-to, so the first thing you have to do is update everything to the latest and greatest version.

sudo apt-get update
sudo apt-get -y dist-upgrade
sudo apt-get -y upgrade
sudo apt-get -y autoremove

And finally reboot the server:

sudo reboot

Install Chrony NTP Server

As we are running a server and not some kind of desktop, keeping time as accurate as possible is always a good idea. Chrony will keep your system clock within a couple of micro seconds (a couple of thousands of a second) of the real time

sudo apt -y install chrony

Wait a couple of minutes and check wether Chrony is getting the correct time from a source:

chronyc sources

This will display a list of the NTP servers Chrony is polling to get its time. One of the lines should start with “^*” tosignal that is the server Chrony is currently getting its time from.

List all time zones, to get the correct description of your own time zone:

sudo timedatectl list-timezones

Set your correct timezone. In my case “Europe/Amsterdam”, so:

sudo timedatectl set-timezone Europe/Amsterdam

Install Postfix

To install postfix enter:

sudo apt -y install postfix 

When asked choose “No configuration”

Install ClamAV

sudo apt install -y clamav clamav-daemon

Enable and start clamd

sudo systemctl enable clamav-daemon 
sudo systemctl enable clamav-freshclam
sudo systemctl start clamav-daemon 

Optional: Extra ClamAV signatures

It does not cost much and gives you a load of extra protection: the 4.000.000 virus/malware signatures of

Install Spamassassin

sudo apt -y install spamassassin 

Optional: Extra definitions for Spamassassin

Here are some links with extra spamassassin definition files:

The extra cf files shoud be placed in /etc/mail/spamassassin

Note for day to day operations: After updatung or installing extra spamassassin cf files MailScanner should be restarted.

Download and install MailScanner


To install MailScanner type:

sudo apt -y install ./MailScanner-5.3.4-3.noarch.deb

Run the configure script:

sudo /usr/sbin/ms-configure

Disable automatic startup of mailscanner for now:

sudo systemctl disable mailscanner

Configure Apparmor

Edit /etc/apparmor.d/usr.sbin.clamd

sudo vi /etc/apparmor.d/usr.sbin.clamd

Add following lines:

# For use with MailScanner
/var/spool/MailScanner/** rw,
/var/spool/MailScanner/incoming/** rw,

As Apparmor can be a pain in the ass: Reboot

sudo reboot

Configure Postfix

To tell Postfix to move all messages to the HOLD queue, do the following: Create the file /etc/postfix/header_checks and add this line:

/^Received:/ HOLD

Create some needed files if they are not already present:

sudo touch /etc/postfix/access
sudo touch /etc/postfix/relay_recipients
sudo touch /etc/postfix/transport
sudo touch /etc/postfix/virtual

Create the Postfix configuration file /etc/postfix/ and add the following lines. Please note the bold lines, you probably need to change something here:

queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
myhostname =

# I only use IPv4, so I only enable IPv4
inet_protocols = ipv4

# Listen on all interfaces
inet_interfaces = all

# The proxy_interfaces parameter specifies the network interface
# addresses that this mail system receives mail on by way of a
# proxy or network address translation unit. This setting extends
# the address list specified with the inet_interfaces parameter.
# You must specify your proxy/NAT addresses when your system is a
# backup MX host for other domains, otherwise mail delivery loops
# will happen when the primary MX host is down.
#proxy_interfaces =
#proxy_interfaces =

# The mydestination parameter specifies the list of domains that this
# machine considers itself the final destination for.
# These domains are routed to the delivery agent specified with the
# local_transport parameter setting. By default, that is the UNIX
# compatible delivery agent that lookups all recipients in /etc/passwd
# and /etc/aliases or their equivalent.
# The default is $myhostname + localhost.$mydomain.  On a mail domain
# gateway, you should also include $mydomain.
# Do not specify the names of virtual domains - those domains are
# specified elsewhere (see VIRTUAL_README).
# Do not specify the names of domains that this machine is backup MX
# host for. Specify those names via the relay_domains settings for
# the SMTP server, or use permit_mx_backup if you are lazy (see
# The local machine is always the final destination for mail addressed
# to user@[] of an interface that the mail system
# receives mail on (see the inet_interfaces parameter).
# Specify a list of host or domain names, /file/name or type:table
# patterns, separated by commas and/or whitespace. A /file/name
# pattern is replaced by its contents; a type:table is matched when
# a name matches a lookup key (the right-hand side is ignored).
# Continue long lines by starting the next line with whitespace.
mydestination = $myhostname, localhost.$mydomain, localhost
#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
#mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain,
#       mail.$mydomain, www.$mydomain, ftp.$mydomain

# The local_recipient_maps parameter specifies optional lookup tables
# with all names or addresses of users that are local with respect
# to $mydestination, $inet_interfaces or $proxy_interfaces.
# If this parameter is defined, then the SMTP server will reject
# mail for unknown local users. This parameter is defined by default.
# To turn off local recipient checking in the SMTP server, specify
# local_recipient_maps = (i.e. empty).
# The default setting assumes that you use the default Postfix local
# delivery agent for local delivery. You need to update the
# local_recipient_maps setting if:
# - You define $mydestination domain recipients in files other than
#   /etc/passwd, /etc/aliases, or the $virtual_alias_maps files.
#   For example, you define $mydestination domain recipients in
#   the $virtual_mailbox_maps files.
# - You redefine the local delivery agent in
# - You redefine the "local_transport" setting in
# - You use the "luser_relay", "mailbox_transport", or "fallback_transport"
#   feature of the Postfix local delivery agent (see local(8)).
# Details are described in the LOCAL_RECIPIENT_README file.
# Beware: if the Postfix SMTP server runs chrooted, you probably have
# to access the passwd file via the proxymap service, in order to
# overcome chroot restrictions. The alternative, having a copy of
# the system passwd file in the chroot jail is just not practical.
# The right-hand side of the lookup tables is conveniently ignored.
# In the left-hand side, specify a bare username, an @domain.tld
# wild-card, or specify a user@domain.tld address.
#local_recipient_maps = unix:passwd.byname $alias_maps
#local_recipient_maps = proxy:unix:passwd.byname $alias_maps
#local_recipient_maps =

# The unknown_local_recipient_reject_code specifies the SMTP server
# response code when a recipient domain matches $mydestination or
# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty
# and the recipient address or address local-part is not found.
# The default setting is 550 (reject mail) but it is safer to start
# with 450 (try again later) until you are certain that your
# local_recipient_maps settings are OK.
unknown_local_recipient_reject_code = 550


# The mynetworks parameter specifies the list of "trusted" SMTP
# clients that have more privileges than "strangers".
# In particular, "trusted" SMTP clients are allowed to relay mail
# through Postfix.  See the smtpd_recipient_restrictions parameter
# in postconf(5).
# You can specify the list of "trusted" network addresses by hand
# or you can let Postfix do it for you (which is the default).
# By allmynetworks_style = subnet), Postfix "trusts" SMTP
# clients in the same IP subnetworks as the local machine.
# On Linux, this does works correctly only with interfaces specified
# with the "ifconfig" command.
# Specify "mynetworks_style = class" when Postfix should "trust" SMTP
# clients in the same IP class A/B/C networks as the local machine.
# Don't do this with a dialup site - it would cause Postfix to "trust"
# your entire provider's network.  Instead, specify an explicit
# mynetworks list by hand, as described below.
# Specify "mynetworks_style = host" when Postfix should "trust"
# only the local machine.
#mynetworks_style = class
#mynetworks_style = subnet
#mynetworks_style = host

# Alternatively, you can specify the mynetworks list by hand, in
# which case Postfix ignores the mynetworks_style setting.
# Specify an explicit list of network/netmask patterns, where the
# mask specifies the number of bits in the network part of a host
# address.
# You can also specify the absolute pathname of a pattern file instead
# of listing the patterns here. Specify type:table for table-based lookups
# (the value on the table right-hand side is not used).
#mynetworks =,
#mynetworks = $config_directory/mynetworks
#mynetworks = hash:/etc/postfix/network_table
mynetworks =,

# This is a space separated list of all the domain names your MailScanner server 
relay_domains =

virtual_alias_maps = hash:/etc/postfix/virtual
transport_maps = hash:/etc/postfix/transport
relay_recipient_maps = hash:/etc/postfix/relay_recipients

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# If you forget the line below, MailScanner wont work
header_checks = regexp:/etc/postfix/header_checks

# As a security measure we hide version info etc. from the standard SMTP header
# so if somebody issues the command "telnet 25"
# your server will reply with " ESMTP"
smtpd_banner = ESMTP
debug_peer_level = 2

debugger_command =
         ddd $daemon_directory/$process_name $process_id & sleep 5

sendmail_path = /usr/sbin/sendmail.postfix

# newaliases_path: The full pathname of the Postfix newaliases command.
# This is the Sendmail-compatible command to build alias databases.
newaliases_path = /usr/bin/newaliases.postfix

# mailq_path: The full pathname of the Postfix mailq command.  This
# is the Sendmail-compatible mail queue listing command.
mailq_path = /usr/bin/mailq.postfix

# setgid_group: The group for mail submission and queue management
# commands.  This must be a group name with a numerical group ID that
# is not shared with other accounts, not even with the Postfix account.
setgid_group = postdrop

# html_directory: The location of the Postfix HTML documentation.
html_directory = no

# manpage_directory: The location of the Postfix on-line manual pages.
manpage_directory = /usr/share/man

mailbox_size_limit = 102400000000
message_size_limit = 1024000000
smtpd_client_restrictions = check_client_access hash:/etc/postfix/access
maximal_queue_lifetime = 14d
delay_warning_time = 12h

smtpd_relay_restrictions =  permit_mynetworks reject_unauth_destination

# For now we disable all the SSL/TLS settings. 
# These are the real settings so when we install the SSLcerts in the right place
# You simply have to uncomment the following lines
#smtpd_use_tls = yes
#smtpd_tls_loglevel = 1
#smtpd_tls_received_header = yes
#smtpd_tls_security_level = may
#smtpd_tls_cert_file = /etc/letsencrypt/live/
#smtpd_tls_key_file = /etc/letsencrypt/live/
#smtpd_tls_CAfile = /etc/letsencrypt/live/
#smtpd_tls_CApath = /etc/ssl/certs
#smtpd_tls_received_header = yes
#smtpd_tls_session_cache_timeout = 3600s
#smtpd_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
#smtpd_tls_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
#smtpd_tls_ciphers = high
#smtpd_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
#smtpd_tls_mandatory_ciphers = high
#smtpd_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL

#smtp_use_tls = yes
#smtp_tls_loglevel = 1
#smtp_tls_received_header = yes
#smtp_tls_security_level = may
#smtp_tls_cert_file = /etc/letsencrypt/live/
#smtp_tls_key_file = /etc/letsencrypt/live/
#smtp_tls_CAfile = /etc/letsencrypt/live/
#smtp_tls_CApath = /etc/ssl/certs
#smtp_tls_session_cache_timeout = 3600s
#smtp_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
#smtp_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
#smtp_tls_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
#smtp_tls_ciphers = high
#smtp_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
#smtp_tls_mandatory_ciphers = high
#tls_preempt_cipherlist = yes
#tls_random_source = dev:/dev/urandom

In the file /etc/postfix/transport add the following for all domains you want to forward to your Exchange/Zimbra server:

Add to the file /etc/postfix/relay_recipients all your users e-mail adresses: x x x

Create if needed the directories for postfix:

sudo mkdir /var/spool/postfix/hold
sudo mkdir /var/spool/postfix/incoming
sudo chown postfix. /var/spool/postfix/hold
sudo chown postfix. /var/spool/postfix/incoming

You will need to ensure that the user “postfix” can write to /var/spool/MailScanner/incoming and /var/spool/MailScanner/quarantine:

sudo chown postfix. /var/spool/MailScanner/incoming
sudo chown postfix. /var/spool/MailScanner/quarantine

Create a script /usr/local/etc/postfix-db

Add the following:

cd /etc/postfix
/usr/sbin/postmap /etc/postfix/virtual
/usr/sbin/postmap /etc/postfix/transport
/usr/sbin/postmap /etc/postfix/access
/usr/sbin/postmap /etc/postfix/relay_recipients

Make it executable:

sudo chmod a+x  /usr/local/etc/postfix-db 

Run it:

sudo   /usr/local/etc/postfix-db  

Set some permissions

sudo mkdir /var/spool/MailScanner/spamassassin
sudo chown postfix.postfix /var/spool/MailScanner/spamassassin

Enable and Restart postfix

sudo systemctl enable postfix
sudo systemctl restart postfix

Configure MailScanner

In your /etc/MailScanner/MailScanner.conf file change the following settings:

Run As User = postfix
Run As Group = postfix
Incoming Queue Dir = /var/spool/postfix/hold
Outgoing Queue Dir = /var/spool/postfix/incoming
MTA = postfix
Clamd Socket = /var/run/clamav/clamd.ctl
SpamAssassin User State Dir = /var/spool/MailScanner/spamassassin

Stop the ClamAV daemon:

sudo systemctl stop clamav-daemon  


sudo vi /etc/clamav/clamd.conf

Change the following:

LocalSocketGroup mtagroup

Change ownership of the files

sudo chown -R postfix.mtagroup  /etc/clamav/ 

Add users Postfix and clamav to mtagroup

sudo usermod -a -G mtagroup postfix
sudo usermod -a -G mtagroup clamav

Restart ClamAV

sudo systemctl enable clamav-daemon
sudo systemctl restart clamav-daemon  

Test-run MailScanner:

sudo MailScanner --lint

Output should give no errors and look something like this:

$ sudo MailScanner --lint
Trying to setlogsock(unix)
Reading configuration file /etc/MailScanner/MailScanner.conf
Reading configuration file /etc/MailScanner/conf.d/README
Read 868 hostnames from the phishing whitelist
Read 5807 hostnames from the phishing blacklists
Checking version numbers…
Version number in MailScanner.conf (5.3.4) is correct.
Your envelope_sender_header in spamassassin.conf is correct.
MailScanner setting GID to (120)
MailScanner setting UID to (115)
Checking for SpamAssassin errors (if you use it)…
Using SpamAssassin results cache
Connected to SpamAssassin cache database
SpamAssassin reported no errors.
Auto: Found virus scanners: clamd
Connected to Processing Attempts Database
Created Processing Attempts Database successfully
There are 0 messages in the Processing Attempts Database
Using locktype = posix
MailScanner.conf says "Virus Scanners = auto"
Found these virus scanners installed: clamd
Filename Checks: Windows/DOS Executable (1
Other Checks: Found 1 problems
Virus and Content Scanning: Starting
Clamd::INFECTED:: Win.Test.EICAR_HDB-1 :: ./1/
Virus Scanning: Clamd found 2 infections
Infected message 1 came from
Virus Scanning: Found 2 viruses
Virus Scanner test reports:
Clamd said " was infected: Win.Test.EICAR_HDB-1"
If any of your virus scanners (clamd)
are not listed there, you should check that they are installed correctly
and that MailScanner is finding them correctly via its virus.scanners.conf

Edit defaults file:

sudo vi /etc/MailScanner/defaults

Change following and save file:


Enable and start MailScanner:

sudo systemctl enable mailscanner
sudo systemctl start mailscanner

Watch the logs for errors

cat /var/log/mail.log