ASCII Smiley Face Daniel Dickinson Mini Headshot
The C Shore

Yet Another Mail Server HOWTO

Version 0.9.3

Note

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

Prerequisites

  • 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 dyn.com, 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).

Packages

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

Stats

  • 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 mail.example.com

SMTP (Inbound/Outbound)

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

    1. Set myhostname to your host’s hostname (e.g. myhostname = mail.example.com)
    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 =
          reject_invalid_hostname,
          reject_unknown_recipient_domain,
          permit_mynetworks,
          reject_unauth_destination
      
      smtpd_recipient_restrictions =
          reject_invalid_hostname,
          reject_unverified_recipient,
          permit_mynetworks,
          reject_rbl_client zen.spamhaus.org,
          permit
      
      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/mail.example.com/fullchain.pem
        smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
        smtpd_tls_security_level = may
        smtp_tls_cert_file = /etc/letsenrypt/live/mail.example.com/fullchain.pem
        smtp_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
        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@example.com, alias1@subdomain1,example, etc to your main mailbox at user@mail.example.com, for however many aliases you want to create.

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

       /^alias1\[@\]/ user@mail.example.com
       /^alias2\[@\]/ user@mail.example.com
       /^alias3\[@\]/ user2@mail.example.com
       /^someotheralias\[@\]/ someotheruser@example,com, user@example.com, user2@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/master.cf, 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. mail.example.com points to 3.48.28.88 (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. example.com) 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/main.cf 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 "Example.com Postmaster" <postmaster@example.com>
    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.

        #!/bin/sh
    
        /usr/bin/wget -q -N -P /etc/opendmarc https://publicsuffix.org/list/effective_tld_names.dat
        /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/main.cf 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 = example.com
    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/mail.example.com/fullchain.pem
     ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
    
  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>@mail.example.com:$(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/maillogconvert.pl standard < /var/log/maillog |
      3. Set LogType=M
      4. Set the following:

        LogFormat="%time2 $email %email_r %host %host_r %method %url %code %bytesd"
        LevelForBrowsersDetection=0
        LevelForOSDetection=0
        LevelForRefererAnalyze=0
        LevelForRobotsDetection=0
        LevelForWormsDetection=0
        LevelForSearchEnginesDetection=0
        LevelForFileTypesDetection=0
        ShowMenu=1
        ShowSummary=HB
        ShowMonthStats=HB
        ShowDaysOfMonthStats=HB
        ShowDaysOfWeekStats=HB
        ShowHoursStats=HB
        ShowDomainsStats=0
        ShowHostsStats=HBL
        ShowAuthenticatedUsers=0
        ShowRobotsStats=0
        ShowEMailSenders=HBML
        ShowEMailReceivers=HBML
        ShowSessionsStats=0
        ShowPagesStats=0
        ShowFileTypesStats=0
        ShowFileSizesStats=0
        ShowBrowsersStats=0
        ShowOSStats=0
        ShowOriginStats=0
        ShowKeyphrasesStats=0
        ShowKeywordsStats=0
        ShowMiscStats=0
        ShowHTTPErrorsStats=0
        ShowSMTPErrorsStats=1
        SiteDomain=mail.example.com
        
        1. Comment out DirIcons=/awstatsicons

Cron Configuration

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

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

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

Attack Detection/Blocking

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

      [postfix]
      enabled = true
    
      [postfix-rbl]
      enabled = true
    
      [dovecot]
      enabled = true
    
      [sieve]
      enabled = true
    
  2. And finally execute systemctl enable fail2ban && systemctl start fail2ban