ASCII Smiley Face Daniel Dickinson Mini Headshot
The C Shore

Yet Another Mail Server HOWTO

Version 0.9.3


This is currently in ‘recipe’ format and doesn’t explain why or go into depth. Future plan for this doc is to be more detailed in those areas.

What you get

  • Mail server
    • IMAP access for clients
    • ManageSieve access for clients (user management of their server-side mail filtering)
    • SMTP for incoming mail
    • Submission port for authenticated users for outgoing mail
    • Unlimited aliases
    • As many actual mailboxes as you can handle (the limit is your available server resources)
  • Security

    • STARTTLS SSL required for all client access
    • Only authorized users can send mail via Submission port (587)
    • No mail relaying
    • Avoid being a backscatter source
      • NB: Assumes you have an wildcard alias and accept all mail for domain(s) serviced by this mail server (i.e. no bounces after queuing)
    • RBL antispam measures
    • Incoming DKIM, DMARC, and SPF (check if others’ mail is spoofed)
    • Outgoing DKIM, DMARC, and SPF (reducing opportunities for others to spoof us, if other servers check for these).
    • Use Let’s Encrypt HTTPS certificates from web server
  • Admin

    • Some attack reduction and blocking
    • Some stats

Not in this document

Could be added

  • POP3 access is an available option
  • Real DB for user database
  • Roundcube/SquirrelMail/etc for webmail access (for myself it represents an unnecessary security risk).
  • Migration from other IMAP/POP3 servers
  • Virtual mailboxes (like All, New, etc)
  • Amavis/Spamassassin/dspam/…: Spam scoring and or rejection (takes more CPU and memory)
  • Using web server only to get Let’s Encrypt certificates for the mail server

Future work

  • DNSSEC in order to enable DANE
  • Add alternate path for web app support (e.g. CGI, uwSGI, etc), e.g. for webmail

Out of Scope (i.e. Lots of Other Documentation Sources for These)

  • Initial host/instance setup
  • General admin utilities and convenience setup


  • An internet accessible host with static IP (and no history of abuse)
    • NB This is a hard requirement for a mail server because most mail servers block email coming from dynamic IP ranges from ISPs to prevent abuse (including this setup).
  • DNS Service (such as, self-hosted, etc)
  • For this HOWTO: CentOS 7, 2 GiB RAM, 10 GiB HD (e.g. virtual HD)
  • Repos
    • Defaults + EPEL (to install epel do yum install epel-release)
  • Let’s Encrypt certificate and renewal mechanism (e.g. such as described in Yet Another Web Server HOWTO).


The following packages need to be installed for this setup (e.g. yum install package1 package2 ...)

Admin Tools

  • policycoreutils
  • policycoreutils-python

Mail Server

  • dovecot
  • dovecot-pigeonhole
  • postfix

Antispam and Antispoofing

  • opendkim
  • opendmarc

Virus Detection / Filtering

  • clamav
  • clamav-scanner
  • clamav-scanner-systemd
  • clamav-milter
  • clamav-update


  • awstats

Attack Detection / Blocking

  • fail2ban
  • fail2ban-firewalld
  • fail2ban-server

First Steps

  1. Configure networking,admin users etc for your host/instance
  2. (Optional) Install your preferred admin/monitoring utilities etc.
  3. Install “Admin Tools” listed above
  4. Add ‘EPEL’ repository listed above

Mail Server Configuration

Prerequisites: Let’s Encrypt certificate for

SMTP (Inbound/Outbound)

  1. Install main “Mail Server” packages above
  2. Edit /etc/postfix/

    1. Set myhostname to your host’s hostname (e.g. myhostname =
    2. Set myorigin to $mydomain
    3. Set mydestination to $myhostname, localhost.$mydomain, localhost.localdomain, localhost
    4. Use local_recipient_maps =
    5. Use unknown_local_recipient_reject_code = 550
    6. Set mynetworks_style to host
    7. Set

      smtpd_relay_restrictions =
      smtpd_recipient_restrictions =
      smtpd_data_restrictions = reject_unauth_pipelining
    8. Comment out relay_domains

    9. Comment out relayhost

    10. Comment out home_mailbox

    11. Comment out mail_spool_directory

    12. Comment out mailbox_command

    13. Set mailbox_transport = lmtp:unix:private/dovecot-lmtp

    14. Comment out smtpd_banner

    15. Set

        smtpd_tls_cert_file = /etc/letsenrypt/live/
        smtpd_tls_key_file = /etc/letsencrypt/live/
        smtpd_tls_security_level = may
        smtp_tls_cert_file = /etc/letsenrypt/live/
        smtp_tls_key_file = /etc/letsencrypt/live/
        smtp_tls_security_level = may
        smtpd_tls_auth_only = yes

Enable Virtual Domains and Virtual Aliases

This allows you to receive mail from e.g., alias1@subdomain1,example, etc to your main mailbox at, for however many aliases you want to create.

  1. Set virtual_alias_domains = ...
  2. Set virtual_alias_maps = regexp:/etc/postfix/virtual
  3. Create a file /etc/postfix/virtual with entries such as:

       /^someotheralias\[@\]/ someotheruser@example,com,,
    1. Execute postmap hash:/etc/postfix/virtual

If you want a regular user (rather than only root) to be able to modify aliases,

  1. Create a group such as virtualadmin using e.g. groupadd virtualadmin
  2. Add users who should be able to modify the virtual table using a command like usermod -a -G virtualadmin usertoadd
  3. Perform chgrp virtualadmin /etc/postfix/virtual
  4. Do chmod 660 /etc/postfix/virtual*

SMTP (Outbound from SMTP AUTH users only)

This will enable the ‘Submission’ port (port 587) such that STARTTLS and authentication against the Dovecot user database (below) is required via SMTP AUTH in order to send emails to the outside world.

  1. To /etc/postfix/, below the line with #tlsproxy ... add:

      submission inet n       -       n       -       -       smtpd
      -o syslog_name=postfix/submission
      -o smtpd_tls_security_level=encrypt
      -o smtpd_sasl_auth_enable=yes
      -o smtpd_sasl_type=dovecot
      -o smtpd_sasl_path=private/auth
      -o smtpd_sasl_security_options=noanonymous
      -o smtpd_sasl_local_domain=$mydomain
      -o smtpd_client_restrictions=permit_sasl_authenticated,reject
      -o smtpd_sender_login_maps=hash:/etc/postfix/virtual
      -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
      -o smtpd_relay_restrictions=permit_sasl_authenticated,reject_unauth_destination

DNS Setup

  • For IPv4 make sure you have DNS A records for you mail host e.g. points to (note this should not be a CNAME for the configuration in this HOWTO).
  • Also make sure reverse DNS entries are present: This is usually a service provided by your hosting provider (since they own the IP block and you can’t setup IP -> Name records without them) or ISP (if hosting on static IPs on an ISP that allows mail hosting on your own hardware).
  • Likewise for DNS for ipv6 if you support it (AAAA records in that case).
  • In addition you need to set up MX entries for your mail hosts (in this HOWTO we’ve only covered a primary mail host/MX). To do this create an an DNS MX type entry for your domain (e.g. with contents 10 mail.example.ccom.. If you have any backup MXes, repeat, replacing the 10 with larger numbers (e.g. 20, 30, …). The number represents the priority for remote mail hosts sending mail to your domain, with lower numbers being higher priority (earlier in the ordering of hosts).
  • The details depend on the DNS provider or server you are using.

Virus Detection / Filtering

SELinux Tweaks

  1. Create a file clamavmilter.te (e.g. in /root) as below

     module clamavmilter 1.0;
     require {
         type antivirus_t;
         type milter_port_t;
         class tcp_socket { name_bind };
     #============= antivirus_t ==============
     allow antivirus_t milter_port_t:tcp_socket name_bind; 
    1. Execute checkmodule -M -m clamavmilter.te -o clamavmilter.mod
    2. Perform semodule_package -o clamavmilter.pp -m clamavmilter.mod
    3. Run semodule -i clamavmilter.pp

Antivirus Mail Filter (Milter)

  1. Install “Virus Detection / Filtering” packages from the list earlier in this document.
  2. Edit /etc/clamd.d/scan.conf with the following (in addition to defaults)
    1. Comment out Example
    2. LogFacility LOG_MAIL
    3. LocalSocket /var/run/clamd.scan/clamd.sock
    4. LocalSocketGroup virusgroup
    5. LocalSocketMode 660
    6. FixStateSocket yes
    7. ExitOnOOM yes
  3. And setsebool -P antivirus_use_jit on
  4. Execute systemctl enable clamd@scan && systemctl restart clamd@scan
  5. Edit /etc/mail/clamav-milter.conf with the following (in addition to defaults)
    1. Comment out Example
    2. MilterSocket inet:8894@localhost
    3. ClamdSocket unix:/var/run/clamd.scan/clamd.sock
    4. OnInfected Reject
    5. AddHeader Add
    6. LogFacility LOG_MAIL
    7. LogInfected Basic
    8. SupportMultipleRecipients yes
  6. Run semanage port --add -t milter_port_t -p tcp 8894
  7. Perform systemctl enable clamav-milter && systemctl restart clamav-milter
  8. To /etc/postfix/ add

       smtpd_milters = inet:localhost:8894
       milter_default_action = tempfail
  9. Do postfix reload

Antispam / Antispoofing

  1. Install “Antispam / Antispoofing” packages from list earlier in this document.
  2. generate keys for your domain(s) and publish your DKIM key in DNS records
  3. Edit /etc/opendkim.conf such that the following are set (in addition to defaults):
    1. Mode sv
    2. ReportAddress " Postmaster" <>
    3. QueryCache yes
  4. Perform systemctl enable opendkim && systemctl start opendkim
  5. Add the following script as /etc/cron.daily/opendmarc_public_suffix_list and make it executable.

        /usr/bin/wget -q -N -P /etc/opendmarc
        /bin/chown opendmarc:opendmarc /etc/opendmarc/effective_tld_names.dat
  6. Edit /etc/opendmarc.conf such that the following are set (in addition to defaults):

    1. AuthservID HOSTNAME
    2. Autorestart true
    3. AutorestartRate 3/1m
    4. PublicSuffixList /etc/opendmarc/effective_tld_names.dat
    5. SyslogFacility mail
  7. Run systemctl enable opendmarc && systemctl start opendmarc

  8. To /etc/postfix/ add (assuming also doing antivirus above)

        smtpd_milters = inet:localhost:8894 inet:localhost:8891 inet:localhost:8893
        non_smtpd_milters = inet:localhost:8891 inet:localhost:8893
        milter_default_action = tempfail
  9. Do postfix reload

IMAP (Mail Storage and Access)

  1. Install dovecot and dovecot-pigeonhole (the latter only if you want Sieve server-side mail filtering)

  2. In /etc/dovecot/dovecot.conf set

    1. protocols = imap lmtp sieve
  3. In /etc/dovecot/conf.d/10-auth set

    1. disable_plaintext_auth = yes
    2. auth_default_realm =
    3. Comment out auth_username_format
    4. auth_mechanisms = plain login
    5. Comment out all #!include auth-....ext
    6. Uncomment #!include auth-passwdfile.conf.ext
  4. In /etc/dovecot/conf.d/10-mail.conf set

    1. mail_location = maildir:~/Maildir (unless you know what you are doing)
  5. In /etc/dovecot/conf.d/10-master.conf set

     service imap-login {
       inet_listener imap {
       inet_listener imaps {
         port = 0
     service lmtp {
       unix_listener /var/spool/postfix/private/dovecot-lmtp {
         mode = 0660
         user = postfix
         group = postfix
     service auth {
       unix_listener auth-userdb {
       unix_listener /var/spool/postfix/private/auth {
         mode = 0600
         user = postfix
         group = postfix
     service auth-worker {
       user = $default_internal_user
  6. In /etc/dovecot/conf.d/10-ssl.conf

     ssl = required
     ssl_cert = </etc/letsencrypt/live/
     ssl_key = </etc/letsencrypt/live/
  7. In /etc/dovecot/conf.d/20-lmtp.conf set

     protocol lmtp {
       mail_plugins = $mail_plugins sieve
  8. In /etc/dovecot/conf.d/managesieve.conf set

     service managesieve-login {
       inet_listener sieve {
         port = 4190
  9. In /etc/dovecot/conf.d/auth-passwdfile.conf.ext set

     passdb {
      driver = passwd-file
      args = scheme=CRYPT username_format=%u /etc/dovecot/users
     userdb {
       driver = passwd-file
       args = username_format=%u /etc/dovecot/users
  10. For each user you want to add do:

      echo "<username>$(doveadm pw -s CRYPT):<uid>:<gid>::/home/vmail/<username>:: >>/etc/dovecot/users", where uid and gid are not used by 'real' users or groups (.e.g. >10000 is usually safe).
  11. Do systemctl enable dovecot && systemctl start dovecot && systemctl restart postfix

Firewall: Allow IMAP, SMTP/Submission, and ManageSieve

  1. Execute the following:

      firewall-cmd --permanent --add-service imap
      firewall-cmd --permanent --add-service smtp
      firewall-cmd --permanent --add-service submission
      firewall-cmd --permanent --new-service=managesieve
      firewall-cmd --permanent --service=managesieve --add-port 5190/tcp
      firewall-cmd --permanent --add-service managesieve
      firewall-cmd --complete-reload

Stats Configuration

Prerequisites: AWStats non-CGI setup such as the one described in Yet Another Web Server HOWTO

AWStats Configuration

  1. In /etc/awstats

    1. For mail server (SMTP) log stats

      1. cp awstats.localhost.localdomain.conf awstats.mail.conf
      2. Set LogFile="/usr/share/awstats/tools/ standard < /var/log/maillog |
      3. Set LogType=M
      4. Set the following:

        LogFormat="%time2 $email %email_r %host %host_r %method %url %code %bytesd"
        1. Comment out DirIcons=/awstatsicons

Cron Configuration

  1. To /etc/cron.hourly/awstats, add:

      /usr/share/awstats/tools/ -config=mail -update -configdir="/etc/awstats" -awstatsprog="/usr/share/awstats/wwwroot/cgi-bin/ -dir=/var/www/vhosts/" >/dev/null
  2. And for full stats for the entire year, to /etc/cron.daily/awstats, add:

      /usr/share/awstats/tools/ -config=mail -update -month=all -configdir="/etc/awstats" -awstatsprog="/usr/share/awstats/wwwroot/cgi-bin/ -dir=/var/www/vhosts/" >/dev/null

Attack Detection/Blocking

  1. To /etc/fail2bain/jail.conf add the following:

      enabled = true
      enabled = true
      enabled = true
      enabled = true
  2. And finally execute systemctl enable fail2ban && systemctl start fail2ban