Configuring mail services (revision 3)

Introduction

This page describes how Alexis Huxley installed and configured his central mail server. Note that:

  • it does not cover configuration of satellelite mail servers, which just relay all mail to the central mail server
  • it assumes that user information and credentials is stored in an LDAP database but should work for other non-NSS/PAM databases
  • it assumes that web services are proxied through a front-end webserver to various service-specific back-end webservers
  • contain network-specific hostnames: my home network is pasta.net; my mail server is mandala.pasta.net; my local DNS server has a CNAME record mail.pasta.net pointing to mandala.pasta.net; my mail server has the internet-visible name mail.pasta.freemyip.com. So adjust these to suit your own environment!

A self-signed certificate

  1. Ensure that the front-end webserver to does not forward requests that it receives over https to the mail server (it is not needed).
  2. Ensure that the front-end webserver forwards requests that it receives over http to the mail server, with a configuration something like this:
    <VirtualHost *:80>
        ServerName mail.pasta.freemyip.com
        ServerAdmin webmaster@dont-use-this-address
        CustomLog /var/log/apache2/mail.pasta.freemyip.com/mail.pasta.freemyip.com-access.log combined2
        ErrorLog /var/log/apache2/mail.pasta.freemyip.com/mail.pasta.freemyip.com-error.log
        LogLevel warn
        ServerSignature Off
    
        <Location />
            ProxyPass http://mail.pasta.net/
            ProxyPassReverse http://mail.pasta.net/
        </Location>
    </VirtualHost>
  3. On the mail server install and run certbot as follows:
    apt -y install certbot
    certbot certonly --standalone -d  mail.pasta.freemyip.com

    (The option --standalone will – whenever requesting/renewing a certificate – start a standalone internal webserver to allow letsencrypt.org authenticate the renewal request; /etc/cron.d/certbot will respect this option when it is used to initially request the certificate.)

  4. Locate the certificate and key that have just been generated by certbot:
    find /etc/letsencrypt/live/ -name fullchain.pem -o -name privkey.pem

    Dovecot

    We set up Dovecot first because it provides user information to Postfix.

    1. Save a copy of a mail from your INBOX into a local file. (This will be uploaded to the IMAP server shortly as a test.)
    2. Before running the next command, note that it will probably fail due to IPv6 being disabled. Don’t panic.
    3. Run:
      apt -y install dovecot-core dovecot-imapd dovecot-ldap
      
    4. Stop dovecot with:
      systemctl stop dovecot
    5. Entirely replace /etc/dovecot/dovecot.conf with the following:
      ######################################################################
      #
      #  WHAT DOES DOVECOT DO?
      #
      ######################################################################
      
      protocols = " imap lmtp"
      
      ######################################################################
      #
      #  WHAT SERVICES DOES DOVECOT PROVIDE TO OTHER PROGRAMS?
      #
      ######################################################################
      
      #  Let postfix delegate authentication to dovecot.
      service auth {
          unix_listener /var/spool/postfix/private/auth {
              group = postfix
              mode = 0660
              user = postfix
          }
      }
      
      #  Let postfix delegate saving mail to dovecot.
      service lmtp {
         unix_listener /var/spool/postfix/private/dovecot-lmtp {
             group = postfix
             mode = 0600
             user = postfix
          }
      }
      
      ######################################################################
      #
      #  HOW ARE MAILS STORED?
      #
      ######################################################################
      
      mail_privileged_group = mail
      mail_location = maildir:/var/mail/maildir/%u:INDEX=/var/mail/indexes/%u
      
      ######################################################################
      #
      #  HOW TO LOOKUP STUFF?
      #
      ######################################################################
      
      #  Look up/validate users using LDAP (LDAP parameters in separate file)
      userdb {
          driver = ldap
          args = /etc/dovecot/dovecot-ldap.conf
      }
      
      #  Authenticate users using LDAP (LDAP parameters in separate file)
      passdb {
        driver = ldap
        args = /etc/dovecot/dovecot-ldap.conf
      }
      #  When authenticating users (possibly on behalf of another program
      #  via the services dovecot provides to other programs), don't use
      #  'username@domain' (if that was even specified) to authenticate, use
      #  'username'.
      auth_username_format = %n
      
      ######################################################################
      #
      #  MISCELLANEOUS
      #
      ######################################################################
      
      #  Don't listen on IPv6
      listen = *
      #  Encryption
      ssl = required
      ssl_cert = </etc/letsencrypt/live/mail.pasta.freemyip.com/fullchain.pem
      ssl_key = </etc/letsencrypt/live/mail.pasta.freemyip.com/privkey.pem
      disable_plaintext_auth = no
      
    6. Create /etc/dovecot/dovecot.conf containing the following:
      ######################################################################
      #
      #  HOW TO CONNECT TO LDAP SERVER?
      #
      ######################################################################
      
      base = ou=Users,dc=pasta,dc=net
      uris = ldaps://ldap.pasta.freemyip.com/
      auth_bind = yes
      auth_bind_userdn = uid=%u,ou=Users,dc=pasta,dc=net
      tls_ca_cert_file = /etc/ssl/certs/ca-certificates.crt
      
      ######################################################################
      #
      #  HOW TO INTERPRET LDAP ATTRIBUTES?
      #
      ######################################################################
      
      #  What to search LDAP for when searching for users
      user_filter = (&(objectClass=posixAccount)(uid=%n))
      #  Map LDAP attributes (home,uid,gid) to Dovecot's names for same.
      user_attrs = homeDirectory=home,uidNumber=uid,gidNumber=gid
      #tls = yes
      
    7. It is probable that the indexes and maildir directories do not exist under /var/mail. Create them and correct permissions on /var/mail that may be caused by it being a new filesystem by running:
      cd /var/mail
      chown root:mail .
      chmod 2775 .
      mkdir indexes maildir
      chown root:mail indexes maildir
      chmod 3777 indexes maildir
    8. Restart stuff:
      service dovecot restart
    9. To test: point your mail client at the file containing a copy of a mail from your INBOX, attempt to upload it to the new mail server, quit the mail client, restart the mail client, point it at the new mail server, check the just-saved mail is accessible.

    Postfix

      1. Run:
        apt -y  install postfix postgrey db-util
        service postfix stop

        (db-util contains db_dump, which is useful for reading some of the databases under /var/lib/postfix.)

      2. Entirely replace /etc/postfix/main.cf with the following:
        ######################################################################
        #
        #  WHO AM I AND WHAT DOMAINS WILL I ACCEPT MAIL FOR?
        #
        ######################################################################
        
        myhostname = mandala.pasta.net
        mydestination = localhost, $myhostname, $mydomain, mail.pasta.freemyip.com, pasta.freemyip.com 
        
        ######################################################################
        #
        #  HOW DO WE RECEIVE MAIL? (REGARDLESS OF TO BE DELIVERED LOCALLY OR RELAYED)
        #
        ######################################################################
        
        #  Later we say that we accept mails from clients connecting to us from 
        #  $mynetworks. Let that be derived from our interfaces' CIDRs.
        mynetworks_style = subnet
        
        #  Later we say that we accept mails from clients that do SASL 
        #  authentication. SASL authentication is delegated to Dovecot, 
        #  with whom we communicate via Unix socket private/auth (relative
        #  to /var/spool/postfix).
        smtpd_sasl_auth_enable = yes 
        smtpd_sasl_type = dovecot
        smtpd_sasl_path = private/auth
        
        #  After 'RCPT TO' has been received, accept/reject mails according 
        #  to this ordered checklist.
        smtpd_recipient_restrictions =
            reject_unverified_recipient,
            permit_sasl_authenticated,
            permit_mynetworks,
            reject_rbl_client zen.spamhaus.org,
            reject_rbl_client bl.spamcop.net,
            check_policy_service inet:127.0.0.1:10023,
            permit
        
        #  Use encyption but don't require it (doing so would contravene RFC2487)
        smtpd_use_tls = yes
        smtpd_tls_security_level = may
        smtpd_tls_cert_file = /etc/letsencrypt/live/mail.pasta.freemyip.com/fullchain.pem
        smtpd_tls_key_file = /etc/letsencrypt/live/mail.pasta.freemyip.com/privkey.pem
        smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
        
        ######################################################################
        #
        #  HOW DO WE DELIVER LOCALLY?
        #
        ######################################################################
        
        #  Accept mail to any user if the domain part of the address matched
        #  one of $mydestination.
        local_recipient_maps = 
        #  Delegate actual delivery to Dovecot. (Use local_transport rather
        #  than mailbox transport because the former prevents Postfix from 
        #  looking up the user's home diretory.)
        local_transport = lmtp:unix:private/dovecot-lmtp
        
        ######################################################################
        #
        #  HOW DO WE SEND MAIL? (REGARDLESS OF WHETHER ORIGINATED LOCALLY OR REMOTELY)
        #
        ######################################################################
        
        #  If we are relaying then what relay do we use?
        relayhost = smtp.gmail.com
        
        #  Google mail relay requires SASL authentication.
        smtp_sasl_auth_enable = yes
        smtp_sasl_password_maps = hash:/etc/postfix/sasl/sasl_passwd
        smtp_sasl_security_options =
        
        #  Google mail relay requires encyption (other mail servers may not care).
        smtp_use_tls = yes
        smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
        smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
        
        #  Check /etc/hosts before checking DNS.
        smtp_host_lookup = native     
        
        ######################################################################
        #
        #  ADDRESS REWRITING
        #
        ######################################################################
        
        #  If an address matches *.$mydomain then change it to $mydomain. 
        masquerade_domains = $mydomain
        #  Which addresses to we masquerade?
        masquerade_classes = envelope_sender, header_sender, 
            header_recipient, envelope_recipient
        #  Adjust incomplete addresses for mails generated locally.
        local_header_rewrite_clients = permit_mynetworks
        #  Redirect if recipient listed in table. alias_maps is only checked
        #  for mails that we have *already* decided will be delivered locally,
        #  wherase virtual_alias_maps is checked for *all* mails. Usually
        #  alias_maps is set to hash:/etc/aliases and virtual_alias_maps to
        #  hash:/etc/postfix/virtual in order to deliberately use two different
        #  tables. However, if we're delivering local mail *not* with the
        #  'local' transport then we're free to use /etc/aliases for whatever
        #  we want. I choose to use it for all aliases.
        alias_maps = 
        virtual_alias_maps = hash:/etc/aliases
        #  Additionally, the 'newaliases' command rehashes the file in
        #  alias_database, so we can use newaliases to rehash our one
        #  unified list.
        alias_database = hash:/etc/aliases
        
        ######################################################################
        #
        #  MISCELLANEOUS
        #
        ######################################################################
        
        compatibility_level = 2
        recipient_delimiter = +
        append_dot_mydomain = no
        message_size_limit = 102400000
        #  Which interfaces to listen on for incoming mails.
        inet_protocols = ipv4
        inet_interfaces = all
        #  Debugging
        #debug_peer_level = 10
        #debug_peer_list = 80.187.84.82
        
      3. Edit /etc/aliases and add aliases for root and any other users, e.g.:
        root: alexis
        www-data:alexis
      4. In this configuration, /etc/aliases is used to redirect mail from one local address to another local address but also to redirect local addresses to remote addresses. Accordingly, add any such aliases to /etc/aliases too, e.g.
        fred: fred.bloggs@gmail.com
      5. Compile the aliases database by running:
        newaliases
      6. If an upstream mail server requires authentication (Gmail does) then specify the username and password for each upstream mail server in /etc/sasl/sasl_passwd. e.g.:
        smtp.gmail.com alexishuxley:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      7. Compile the sasl_passwd database by running:
        postmap hash:/etc/postfix/sasl_passwd
      8. When running a mail client at work that attempts to connect to port 25 on this mail server, it can be that work’s firewalls block access to remote hosts on this port. Therefore enable SMTPS by editing /etc/postfix/master.cf and uncommenting the line:
        smtps     inet  n       -       y       -       -       smtpd
      9. Run:
        service postfix restart
      10. To test: check IMAP access; send mail local to local; local to remote; remote to local; remote to remote (i.e. a remote mail server is to authenticate itself and send a mail to be relayed to another remote mail server); also try mobile clients like K-9 (it needs to be told to connect to the SMTP server using STARTTLS on port 465).

    1. If the system uses PCMS then /etc/postfix/main.cf should somehow be saved into the site-config module.
    2. Install pflogsumm and get it called by editing /etc/logrotate.d/rsyslog and adding something like:
      {
          ...
          postrotate
              ...
              pflogsumm < /var/log/mail.info.1 | mailx -s "pflogsumm report" alexis
          ...
      }
      

    Switchover

    1. Create a temporary key pair to allow ssh between the old mailserver and the new mailserver.
    2. On the old mailserver run:
      systemctl stop postfix
      systemctl stop dovecot
      systemctl disable postfix
      systemctl disable dovecot

      (Note that we don’t actually shut down the old mailserver because it is also the Jabber server, though that will be moved to another container in the future.)

    3. On the new mailserver run:
      systemctl stop postfix
      systemctl stop dovecot
    4. Transfer the mail folder.
    5. If you need to change your DNS database (e.g. change target of ‘mail’ CNAME; change MX) then do so now.
    6. If you have rules on your firewall for incoming mail to update, then update them now.
      1. Create a script to instruct Dovecot and Postgix to reload their configurations when the Let’s Encrypt certificate gets renewed:
        {
            echo '#!/bin/sh'
            #  Copying the certificates into /etc/postfix and /etc/dovecot is not necessary.
            echo 'systemctl reload dovecot'
            echo 'systemctl reload postfix'
        } > /etc/letsencrypt/renewal-hooks/deploy/dovecot-and-postfix
        chmod 755 /etc/letsencrypt/renewal-hooks/deploy/dovecot-and-postfix

      See also