Set up a transactional mail server

A [//en.wikipedia.org/wiki/Email_marketing#Transactional_emails transactional email] is generated by many popular packages, typically for account registration confirmation, topic reply notifications, password retrieval, and other automated uses such as sending system notification messages to administrators. The emails will commonly be sent from an email address that is a non-functioning email account used exclusively for sending, such as NO-REPLY@example.com.

The PHP function is typically the default method that PHP packages will use for sending transactional mail. This function will use the server's sendmail-compliant [//en.wikipedia.org/wiki/Message_transfer_agent MTA] to send the mail, thus necessitating a sendmail-compliant MTA to be installed. An MTA can be used to send mail directly from the server or can be configured to send mail through a [//en.wikipedia.org/wiki/Smart_host smart host] to reduce server and admin overhead.

Most of the popular PHP packages will include an option or have a plugin available to send transactional email through an SMTP server, eliminating the requirement to install any type of MTA for the purpose of supporting email generated by the package. However, without an MTA installed, system messages cannot be sent to the administrators.

Transactional email via MTA
Log in as root and install the necessary packages:

username@servername:~$ sudo -i root@servername:~# aptitude install postfix opendkim opendkim-tools

Selected 'Internet site' during the  install.

Configure OpenDKIM
The  package will add a DKIM signature to configured outbound emails. Receiving servers can compare this signature with the corresponding DNS DKIM record and verify messages as coming from an approved server and unaltered.

root@servername:~# mkdir -p /etc/opendkim/keys/example.com root@servername:~# opendkim-genkey -r -b 2048 -h sha256 -d example.com -s selector -D /etc/opendkim/keys/example.com

Replace  as desired, as it is only used to identify the key the server will call. Many admins will simply use the date the key was created (e.g. '20140510') or, when multiple servers are being used, the server name (e.g. 'mta05'), though none of this is standardized or required - some simply use 'mail', or don't specify anything when running the command, leaving the selector of 'default'. The command creates two files,  and. The  file contains the private key while   contains the basis for the DNS TXT record that will be created in a later step.

Note that the DKIM standard currently recommends a maximum key size of 2048 bits, so using a larger key size will likely cause the DKIM test to fail on many servers, while using a key smaller 1024 bits is not only not recommended, but has been demonstrated to be insecure.

KeyTable
Create the tables that  will use:

root@servername:~# nano /etc/opendkim/KeyTable

Add: example.com example.com:selector:/etc/opendkim/keys/example.com/selector.private

Since this server is being configured to have one domain serve mail for multiple domains, only one private key is being used.

SigningTable
root@servername:~# nano /etc/opendkim/SigningTable

Add: *@example.com example.com *@subdomain.example.com example.com *@example.org example.com

The second and third entries demonstrate how additional domains served by the mail server would be added to the signing table.

TrustedHosts
root@servername:~# nano /etc/opendkim/TrustedHosts

127.0.0.1 ::1 localhost example.com

Configure opendkim.conf
Set permissions on the directory, archive the default  before creating a new one:

root@servername:~# chown -R opendkim:opendkim /etc/opendkim root@servername:~# mv /etc/opendkim.conf /etc/original.opendkim.conf root@servername:~# nano /etc/opendkim.conf

Add to the new file: Syslog                 yes UMask                  002 SubDomains             yes AutoRestart            yes Background             yes Canonicalization       relaxed/relaxed DNSTimeout             5 Mode                   sv SignatureAlgorithm      rsa-sha256 ExternalIgnoreList     refile:/etc/opendkim/TrustedHosts InternalHosts          refile:/etc/opendkim/TrustedHosts KeyTable               refile:/etc/opendkim/KeyTable SigningTable           refile:/etc/opendkim/SigningTable LogWhy                 Yes PidFile                /var/run/opendkim/opendkim.pid Socket                 local:/var/spool/postfix/opendkim/opendkim.sock SyslogSuccess          Yes TemporaryDirectory     /var/tmp UserID                 opendkim:opendkim OversignHeaders        From
 * 1) This is a basic configuration that can easily be adapted to suit a standard
 * 2) installation. For more advanced options, see opendkim.conf(5) and/or
 * 3) /usr/share/doc/opendkim/examples/opendkim.conf.sample.
 * 1) Log to syslog
 * 1) Required to use local socket with MTAs that access the socket as a non-
 * 2) privileged user (e.g. Postfix)
 * 1) Commonly-used options
 * 1) Additional OpenDKIM options
 * 1) Always oversign From (sign using actual From and a null From to prevent
 * 2) malicious signatures header fields (From and/or others) between the signer
 * 3) and the verifier.  From is oversigned by default in the Debian package
 * 4) because it is often the identity key used by reputation systems and thus
 * 5) somewhat security sensitive.

Though most of the settings are fairly self-explanatory, it is a good idea to become familiar with the various settings to reduce the time spent troubleshooting why other mail servers are failing DKIM checks on mail sent from the server. One particular setting to note is  (everything after the @ symbol in an email address) being set to. The above configuration has  set to   as many popular PHP packages will default send email from the domain for which they are configured (e.g., wordpress@www.example.com).

Create the directory for the domain socket specified in, make the   user a member of the   group so it can edit  , and restart the service:

root@servername:~# mkdir /var/spool/postfix/opendkim root@servername:~# chown opendkim:root /var/spool/postfix/opendkim root@servername:~# service opendkim restart

Configure postfix
Following the below steps will configure  exclusively for sending and will reject email not coming from one of the loopback addresses. Additionally,  has not been configured to permit inbound connections commonly monitored by , so attempts to send mail to the server will fail to at least these two considerations.

Configure main.cf
The  file is the primary configuration file for Postfix, and it is enormous. Archive the original version, then create a new one:

root@servername:~# mv /etc/postfix/main.cf /etc/postfix/original.main.cf root@servername:~# nano /etc/postfix/main.cf

Add the following: smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) biff = no append_dot_mydomain = no readme_directory = no compatibility_level = 2 smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_use_tls=yes smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtp_tls_security_level = may smtp_tls_note_starttls_offer = yes smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination myhostname = example.com alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname mydestination = relayhost = mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mailbox_size_limit = 0 recipient_delimiter = + inet_interfaces = 127.0.0.1, [::1] inet_protocols = all non_smtpd_milters = unix:opendkim/opendkim.sock smtpd_milters = unix:opendkim/opendkim.sock
 * 1) See /usr/share/postfix/main.cf.dist for a commented, more complete version
 * 1) Debian specific:  Specifying a file name will cause the first
 * 2) line of that file to be used as the name.  The Debian default
 * 3) is /etc/mailname.
 * 4) myorigin = /etc/mailname
 * 1) appending .domain is the MUA's job.
 * 1) Uncomment the next line to generate "delayed mail" warnings
 * 2) delay_warning_time = 4h
 * 1) See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
 * 2) fresh installs.
 * 1) TLS parameters
 * 1) See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
 * 2) information on enabling SSL in the smtp client.

Configure master.cf
Another important and huge configuration file is. Make an archive of the original version and make a new one:

root@servername:~# mv /etc/postfix/master.cf /etc/postfix/original.master.cf root@servername:~# nano /etc/postfix/master.cf

Add to the file:

# # smtp     inet  n       -       y       -       -       smtpd pickup   unix  n       -       y       60      1       pickup cleanup  unix  n       -       y       -       0       cleanup qmgr     unix  n       -       n       300     1       qmgr tlsmgr   unix  -       -       y       1000? 1      tlsmgr rewrite  unix  -       -       y       -       -       trivial-rewrite bounce   unix  -       -       y       -       0       bounce defer    unix  -       -       y       -       0       bounce trace    unix  -       -       y       -       0       bounce verify   unix  -       -       y       -       1       verify flush    unix  n       -       y       1000? 0      flush proxymap unix  -       -       n       -       -       proxymap proxywrite unix -      -       n       -       1       proxymap smtp     unix  -       -       y       -       -       smtp relay    unix  -       -       y       -       -       smtp -o syslog_name=postfix/$service_name showq    unix  n       -       y       -       -       showq error    unix  -       -       y       -       -       error retry    unix  -       -       y       -       -       error discard  unix  -       -       y       -       -       discard local    unix  -       n       n       -       -       local virtual  unix  -       n       n       -       -       virtual lmtp     unix  -       -       y       -       -       lmtp anvil    unix  -       -       y       -       1       anvil scache   unix  -       -       y       -       1       scache # # # # # # # # # # # # # # # # # #
 * 1) Postfix master process configuration file.  For details on the format
 * 2) of the file, see the master(5) manual page (command: "man 5 master" or
 * 3) on-line: http://www.postfix.org/master.5.html).
 * 1) Do not forget to execute "postfix reload" after editing this file.
 * 1) service type  private unpriv  chroot  wakeup  maxproc command + args
 * 2)               (yes)   (yes)   (no)    (never) (100)
 * 1)               (yes)   (yes)   (no)    (never) (100)
 * 1) smtp     inet  n       -       y       -       1       postscreen
 * 2) smtpd    pass  -       -       y       -       -       smtpd
 * 3) dnsblog  unix  -       -       y       -       0       dnsblog
 * 4) tlsproxy unix  -       -       y       -       0       tlsproxy
 * 5) submission inet n      -       y       -       -       smtpd
 * 6)  -o syslog_name=postfix/submission
 * 7)  -o smtpd_tls_security_level=encrypt
 * 8)  -o smtpd_sasl_auth_enable=yes
 * 9)  -o smtpd_tls_auth_only=yes
 * 10)  -o smtpd_reject_unlisted_recipient=no
 * 11)  -o smtpd_client_restrictions=$mua_client_restrictions
 * 12)  -o smtpd_helo_restrictions=$mua_helo_restrictions
 * 13)  -o smtpd_sender_restrictions=$mua_sender_restrictions
 * 14)  -o smtpd_recipient_restrictions=
 * 15)  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 * 16)  -o milter_macro_daemon_name=ORIGINATING
 * 17) smtps    inet  n       -       y       -       -       smtpd
 * 18)  -o syslog_name=postfix/smtps
 * 19)  -o smtpd_tls_wrappermode=yes
 * 20)  -o smtpd_sasl_auth_enable=yes
 * 21)  -o smtpd_reject_unlisted_recipient=no
 * 22)  -o smtpd_client_restrictions=$mua_client_restrictions
 * 23)  -o smtpd_helo_restrictions=$mua_helo_restrictions
 * 24)  -o smtpd_sender_restrictions=$mua_sender_restrictions
 * 25)  -o smtpd_recipient_restrictions=
 * 26)  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
 * 27)  -o milter_macro_daemon_name=ORIGINATING
 * 28) 628      inet  n       -       y       -       -       qmqpd
 * 1) qmgr    unix  n       -       n       300     1       oqmgr
 * 1)       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
 * 1) Interfaces to non-Postfix software. Be sure to examine the manual
 * 2) pages of the non-Postfix software to find out what options it wants.
 * 1) pages of the non-Postfix software to find out what options it wants.
 * 1) Many of the following services use the Postfix pipe(8) delivery
 * 2) agent.  See the pipe(8) man page for information about ${recipient}
 * 3) and other message envelope options.
 * 1) maildrop. See the Postfix MAILDROP_README file for details.
 * 2) Also specify in main.cf: maildrop_destination_recipient_limit=1
 * 1) maildrop unix  -       n       n       -       -       pipe
 * 2)  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
 * 1) Recent Cyrus versions can use the existing "lmtp" master.cf entry.
 * 1) Specify in cyrus.conf:
 * 2)   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
 * 1) Specify in main.cf one or more of the following:
 * 2)  mailbox_transport = lmtp:inet:localhost
 * 3)  virtual_transport = lmtp:inet:localhost
 * 1) Cyrus 2.1.5 (Amos Gouaux)
 * 2) Also specify in main.cf: cyrus_destination_recipient_limit=1
 * 1) cyrus    unix  -       n       n       -       -       pipe
 * 2)  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
 * 1) Old example of delivery via Cyrus.
 * 1) Old example of delivery via Cyrus.
 * 1) old-cyrus unix -       n       n       -       -       pipe
 * 2)  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
 * 1) See the Postfix UUCP_README file for configuration details.
 * 1) uucp     unix  -       n       n       -       -       pipe
 * 2)  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
 * 1) Other external delivery methods.
 * 1) ifmail   unix  -       n       n       -       -       pipe
 * 2)  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
 * 3) bsmtp    unix  -       n       n       -       -       pipe
 * 4)  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
 * 5) scalemail-backend unix -       n       n       -       2       pipe
 * 6)  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
 * 7) mailman  unix  -       n       n       -       -       pipe
 * 8)  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
 * 9)  ${nexthop} ${user}

Postfix user groups
The  user will need to be added to the   group to support domain sockets:

root@servername:~# usermod -G opendkim postfix root@servername:~# service postfix restart

Configure DNS records
There are three different DNS record entries that can be made to reduce the chances of mail being declared as spam, the SPF record, DKIM record, and DMARC record.

Configure SPF records
SPF records are used to verify that emails originating from a domain are permitted to be sent on behalf of the domain. This means there is no actual local server configuration on a transactional mail server.

Log into your DNS server and create a text record with at least the following for the tld (e.g., example.com):

v=spf1 a -all

The above record will inform receiving servers that only the servers listed in the 'a' record (including 'aaaa' record for IPv6 addresses) are approved to send mail. The  mechanism means nothing else is approved to send mail. Note that the softfail mechanism used instead of the hardfail  means the record will be ignored by most popular mail servers resulting in an increased chance of mail from the sending server arriving in spam folders.

If it is desired to send mail from a subdomain from the same server, it is necessary to create an SPF record in addition to the main domain. A simple solution is to use the  mechanism. This mechanism means that the SPF record of the target domain will be included in the current domains SPF record.

Example for www.example.com:

v=spf1 include:example.com -all

Configure DKIM record
DKIM can only function with a valid DNS TXT record. In the DNS manager for your DNS server, make the following new TXT record:

Enter into the optional subdomain field: selector._domainkey

Enter the contents of  into the body of the record using the format below (i.e. only the contents between   and  ):

v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuX4vPnLopTAIMFnnuP4CCEfE/FtQO0mi77voGsWSvHQfvFMIkQ3W3VmeAEiSJd6SVkL/Ojr30ag2i6wA3NTU+1ndfgL371zKx4gDAnewoRA4N2P05HPUNe10DE+m4xnwB6zsQnnPJ3EgKIW6W/v+fN/EzTfeJo5UmxiAoFRIq5hgyeHHCI8aKMQLCmWhb/Pz22MiqRHxV91xmTMLx/e3BIsplcOmQjlOyGagoIZJxpcTlf9OiSWks2a5kHXEN40eh99zkPGInqTrbhDog+cn/mvPgY0uIznx1i/ubRQFtYaH5t6vCu5uSMEQjcTQnWRLI9Qt7Mp15hOMrpkKv4SPzwIDAQAB

Keys can be verified using tools such as the one at DKIM Core. Note that some DNS servers may not like the length of the key string, so it may be necessary to use one of the record formatting methods recommended in the OpenDKIM README.

Configure DMARC record
DMARC records are used to inform receiving servers of what the domain owner's policies are for handling DKIM and SPF record checks. Similar to SPF records, there is no configuration for the local server.

Generating the DMARC record is easiest with an online tool, such as the Kitterman DMARC Record Assistant. The record generated there can be added to a DNS text record for the domain using the subdomain _dmarc.

Note that the  as a policy is much less substantial in preventing email from arriving in spam folders when compared to the   policy. However, with the  policy enforced, the email will not arrive when there is some sort of server configuration issue (e.g.,   stops signing messages or a PHP package or plugin upgrade adds a previously unused subdomain to the email address).

The RUA and FUA reports must be sent to either an email address for the domain the record applies to (e.g., hostmaster@example.com) or if a different domain is used, then the DMARC record for that domain must state it is permitted to receive reports for the example.com domain, else the reports will not be sent.

A configuration for testing:

v=DMARC1; p=none; adkim=s; aspf=s

It may be informative to change from strict to relaxed.

A simple, strict and brutal record that would not generate reports:

v=DMARC1; p=reject; adkim=s; aspf=s