Postfix server setup

https://www.njae.me.uk/Postfix_server_setup

This step of the howto assumes you’ve already set up Dovecot to provide authentication and local delivery services. This page describes the setup for Postfix, TLS, and Amavis + SpamAssassin + ClamAV. Dovecot, and Sieve setup are on theDovecot server setup page. Squirrelmail setup is described in the Web server setup page, along with the HTTPS configuration.

The basic sources for setting up Postfix and Dovecot are the basic Postfix howto and the Postfix virtual user howto on the Ubuntu wiki.

Basic installation

  • Install postfix
root@server:~# aptitude install postfix mailx 
  • Update /etc/postfix/main.cf
mydomain = domain1.com   # Change this line for your domain
myorigin = $mydomain
myhostname = mail.$mydomain  # Change this for your mailserver's name

mydestination = $myhostname localhost.$mydomain localhost
relaydomains = $mydomain
relayhost =
mynetworks = 127.0.0.0/8 192.168.1.0/24
smtpd_recipient_restrictions = permit_mynetworks reject_unauth_destination

## Anti-spammer measures
# Don't talk to mail systems that don't know their own hostname.
smtpd_helo_restrictions = reject_unknown_helo_hostname

# Don't accept mail from domains that don't exist.
smtpd_sender_restrictions = reject_unknown_sender_domain

# Block clients that speak too early.
smtpd_data_restrictions = reject_unauth_pipelining

proxy_interfaces = 1.2.3.4  # My external IP
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

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

mailbox_size_limit = 0
recipient_delimiter = .  # Make sure you change this from a '+'
inet_interfaces = all

home_mailbox = Maildir/

virtual_mailbox_domains = /etc/postfix/vhosts
virtual_mailbox_base = /var/vmail
virtual_mailbox_maps = hash:/etc/postfix/vmaps
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

virtual_alias_maps = hash:/etc/postfix/valiases
  • Tell Postfix about the domains it should service by creating /etc/postfix/vhosts
domain1.com
domain2.com
  • Tell Postfix about the users in those domains by creating /etc/postfix/vmaps
user1@domain1.com  domain1.com/user1/mail/
user2@domain1.com  domain1.com/user2/mail/
user1@domain2.com  domain2.com/user1/mail/
user3@domain2.com  domain2.com/user3/mail/
Then create the hash file:
root@server:~# postmap /etc/postfix/vmaps
(this command needs to be repeated every time /etc/postfix/vmaps is changed)
  • Create aliases for the users in /etc/postfix/valiases
root@domain1.com         user1@domain1.com
postmaster@domain1.com   user1@domain1.com
postmaster@domain2.com   user1@domain1.com
user2@domain1.com        user2@domain2.com
Then create the hash file:
root@server:~# postmap /etc/postfix/valiases
(this command needs to be repeated every time /etc/postfix/valiases is changed)
  • Create /etc/aliases
root:   user1
clamav: root
amavis: root
postmaster:     root
and then create the database
root@server:~# newaliases
  • The virtual mailbox directory structure should be created for each user when they receive their first email.
  • Get Postfix to reload the updated configuration files
root@server:~#postfix reload
  • Test that Postfix is running properly. First, use telnet to connect to postfix
root@server:~# telnet localhost 25
Postfix will respond
Trying 127.0.0.1...
Connected to mail.server.org.
Escape character is '^]'.
220 localhost.localdomain ESMTP Postfix (Ubuntu)
then type the following
ehlo localhost
mail from: root@localhost
rcpt to: user1@domain1.com
data
Subject: My first mail on Postfix

Hi,
Are you there?
regards,
Admin
. (Type the .[dot] in a new Line and press Enter )
quit
then check that the message is delivered properly into the correct mailbox
root@server:~# ls /home/vmail/domain1/user1
  • Then test that all the mail transport is working, using the mail command line utility
root@server:~# mail user2@domain1.com
and again, check that the mail appears in the filesystem.

Adding TLS

Now to enable TLS, which encrypts communication between MTAs, preventing eavesdropping. We’ll use the certificates generated previously. All that’s needed is to modify the /etc/postfix/main.cf file to point to the certificates.

# TLS parameters
smtpd_tls_cert_file=/etc/letsencrypt/live/webmail.domain.tld/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/webmail.domain.tld/privkey.pem

# smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache

and then reload Postfix

root@server:~# postfix reload

Check it’s working by telnetting into Postfix

root@server:~# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mail.domain.tld ESMTP Postfix (Ubuntu)
ehlo localhost

and check that the line

250-STARTTLS

appears in the output. Use quit to end the session.

Clients should connect on port 25, using TLS security.

Allowing off-site users with SASL

As it stands, the server will only forward mail sent from the LAN. That’s fine, but if you want to send mail from other devices via this SMTP server, you need to put some authentication on it. SASL is the general term for authentication on mail servers.

Assuming you have Dovecot set up with some users (up to the ‘Testing Dovecot‘ stage, you can use Dovecot’s authentication engine for authenticating users of the SMTP server.

First, extend the Dovecot configuration file /etc/dovecot/conf.d/10-master.conf with the unix_listener section below so that it accepts authentication requests from Postfix.

service auth {
  unix_listener auth-userdb {
    mode = 0660
    user = vmail
    group = vmail
  }

  # Postfix smtp-auth                                                                                                 
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    user = postfix
    mode = 0666
  }

  # Auth process is run as this user.                                                                                 
  #user = $default_internal_user                                                                                      
}

service auth-worker {
  # Auth worker process is run as root by default, so that it can access                                              
  # /etc/shadow. If this isn't necessary, the user should be changed to                                               
  # $default_internal_user.                                                                                           
  #user = root                                                                                                        
  user = $default_internal_user
}

Next, make Postfix authenticate users. Modify /etc/postfix/main.cf to include the lines

smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination

smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache

Restart both Dovecot and Postfix:

root@server:~# service dovecot reload
root@server:~# postfix reload

Mail clients can now post messages through the SMTP server, so long as they give a valid username/password combination that’s recognised by Dovecot.

Amavis + ClamAV + SpamAssassin

Both ClamAV and SpamAssassin need to be introduced into Postfix’s message-handlng queue to do their things with messages. The idea is that Postfix will take the message out of it’s queue and hand it over the filter program (listening on the loopback interface), who will return it the same way after doing its thing. The trouble is, Postfix can only have one such filter, so Amavis is needed to act as a wrapper around them both. Here is how to do it. For instructions, I basically followed the ones on the Ubuntu wiki.

  • Create the file /etc/mailname containing
mydomain.com
  • Install the packages:
root@server:~# aptitude install amavisd-new spamassassin clamav-daemon pyzor razor
root@server:~# aptitude install arj bzip2 cabextract cpio file gzip lha nomarch pax rar unrar unzip zip

ClamAV

  • Add the ClamAV user to the Amavis group, and vice versa
root@server:~# adduser clamav amavis
root@server:~# adduser amavis clamav

The remainder of ClamAV’s default settings are adequate

SpamAssassin

  • Edit /etc/default/spamassassin to activate the Spamassassin daemon. Change ENABLED=0 to:
ENABLED=1
and, to allow automatic rule updates, change CRON=1
CRON=1

Then start Spamassassin:

root@server:~# service spamassassin start

Amavis

  • Edit /etc/amavis/conf.d/15-content_filter_mode to enable spam filtering
#
# Default SPAM checking mode
# Uncomment the two lines below to enable it back
# 

@bypass_spam_checks_maps = (
   \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);
(don’t enable virus checking yet, as ClamAV 0.90, as included in Ubuntu 7.04, doesn’t seem to work very well)
  • Edit both /etc/amavis/conf.d/20-debian-defaults and /etc/amavis/conf.d/21-ubuntu-defaults to determine the fate of spam and virus-laden mails
$final_virus_destiny      = D_PASS;
$final_banned_destiny     = D_PASS;
$final_spam_destiny       = D_PASS;
$final_bad_header_destiny = D_PASS;
(in other words, don’t discard any mail for the moment)
  • Restart Amavis
root@server:~# service amavis restart

Postfix

  • Now, include Amavis in Postfix’s message queue. Modify /etc/postfix/main.cf to use Amavis
# Spam and virus filtering
content_filter=smtp-amavis:[127.0.0.1]:10024
  • Modify /etc/postfix/master.cf to handle the return from Amavis
# 
# Amavis spam and virus filtering
#

# Sending to Amavis
smtp-amavis unix        -       -       -       -       2       smtp
  -o smtp_data_done_timeout=1200
  -o smtp_send_xforward_command=yes
  -o disable_dns_lookups=yes
  -o max_use=20

# Returning from Amavis
127.0.0.1:10025 inet    n       -       -       -       -       smtpd
  -o content_filter=
  -o local_recipient_maps=
  -o relay_recipient_maps=
  -o smtpd_restriction_classes=
  -o smtpd_delay_reject=no
  -o smtpd_client_restrictions=permit_mynetworks,reject
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=permit_mynetworks,reject
  -o smtpd_data_restrictions=reject_unauth_pipelining
  -o smtpd_end_of_data_restrictions=
  -o mynetworks=127.0.0.0/8
  -o smtpd_error_sleep_time=0
  -o smtpd_soft_error_limit=1001
  -o smtpd_hard_error_limit=1000
  -o smtpd_client_connection_count_limit=0
  -o smtpd_client_connection_rate_limit=0
  -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks 
  • Reload the Postfix configuration:
root@server:~# postfix reload

Enable Dovecot LDA

We now want Postfix to use the Dovecot LDA for local mail delivery and filtering.

  • Modify /etc/postfix/master.cf to include the line
#
# dovecot as a local delivery agent 
#
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -a ${recipient} -d ${user}@${nexthop}
  • Modify /etc/postfix/main.cf to include the line
## Local Delivery

dovecot_destination_recipient_limit = 1
mailbox_transport = dovecot
virtual_transport = dovecot
  • Reload the Postfix configuration:
root@server:~# postfix reload

Block addresses that are known to spammers

The recipient_delimiter option on Postfix is rather neat. Given a recipient_delimiter of ‘.‘, messages to user.ext1@domain1.com are handled in an interesting way. If user.ext1@domain1.com is a valid user, the message is put in their mailbox. If user.ext1@domain1.com isn’t a valid user, Postfix will drop everything in the username after the delimiter and retry the delivery to user@domain1.com. This means that you can create specialised-use email addresses and have them all come to the same mailbox. It also means you can create ‘disposable’ email addresses. That also requires some method of disposing of those addresses when the time comes.

The disposal is done by checking both the headers of incoming emails the RCPT TO addresses, and discarding or rejecting those that are to a disposable address.

  • Add the following to /etc/postfix/main.cf
# block addresses that are known to spammers
header_checks = regexp:/etc/postfix/header_checks

smtpd_recipient_restrictions =
                            check_recipient_access hash:/etc/postfix/recipient_checks # Add this directive
                            permit_sasl_authenticated
                            permit_mynetworks
                            reject_unauth_destination
  • Create the file /etc/postfix/header_checks
if /^To:/
/^To:.*(user1\.ext1@domain1\.com)/
  reject Recipient address rejected: User unknown in virtual mailbox table
/^To:.*(user1\.ext2@domain1\.com)/
  reject Recipient address rejected: User unknown in virtual mailbox table
endif
  • Create the file /etc/postfix/recipient_checks
# Reject messages with these recipient addresses
user.banned2@example.com REJECT
user.banned@example.com REJECT
user.banned3@example.com REJECT
user.banned4@example.com REJECT
person.banned@example.com REJECT
  • Reload the configuration
root@server:~# postmap /etc/postfix/recipient_checks
root@server:~# postfix reload

Configure Postfix as a backup MX server for other domains

Having backup mail servers is always good, so it’s only fair to offer the same service to others in exchange. This is how you do it.

  • In /etc/postfix/main.cf, modify smtpd_recipient_restrictions and add the relay_domains and smtpd_relay_restrictions
smtpd_recipient_restrictions =
                            check_recipient_access hash:/etc/postfix/recipient_checks

# $mydomain included as a virtual domain below                                                                                                                                                                                                             
relay_domains = otherdomain1.com otherdomain2.com

# Include the relay_recipients to only relay certain addresses at the relayed domains
# relay_recipient_maps = hash:/etc/postfix/relay_recipients                                                                                                                                                                                                
smtpd_relay_restrictions =  permit_sasl_authenticated
                            permit_mynetworks
                            reject_unauth_destination
  • Reload the configuration
root@server:~# postfix reload

However, there is a problem with queue lifetimes. With this setup, messages held in the backup role will only be stored on this server for the standard three days. If you’re acting as backup for a privately-run mail server, this may not be long enough (the other site’s owner could be away on holiday, for example, and may not get back within three days). Simply increasing the queue lifetime means that undeliverable messages to third parties are not reported back for a long time, which isn’t that great.

The way to fix is, apparently, is to have two Postfix instances. One instance handles the normal traffic, with a short queue time, while the other handles traffic for the backed-up mail host; the latter Postfix instance has a long queue time and so can keep the mail for a while. I’ve not attempted to do this yet, as the above settings work OK for me.

Open submission port (587)

Some ISPs prevent access to port 25, the normal mail transfer port. To allow clients on these networks to still connext to this server, we open port 587 for SMTP mail submission.

Modify /etc/postfix/master.cf to include:

submission inet n       -       -       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  # -o smtpd_client_restrictions=permit_sasl_authenticated,permit_mx_backup,reject
  -o milter_macro_daemon_name=ORIGINATING

You may also need to modify /etc/postfix/main.cf to include

data_directory = /var/lib/postfix
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
tls_random_exchange_name = ${data_directory}/prng_exch

And don’t forget to open the port in the firewall and on the router.

Authenication and verification

There are a number of ways to authenticate your email servers with others. This should prevent spam messages.

SPF

This is a mechanism for the receiving MTA to check that messages it receives came from an MTA that was authorised to send them. It uses DNS records to list the allowed domains. You simply set a DNS record to allow other MTAs to check mail that purports to come from you. To check mail that comes from other domains to your own MTA, you need to install an SPF checking tool and integrate it into Postfix.

  • Add the following as a TXT record to the DNS entry for each domain you send mail.
v=spf1 mx a mx:backup.com ~all
This tells other mail servers that mail servers listed the MX portion of your DNS entry can send email for your domain. It also tells those other servers to accept email sent from your IP number and servers listed in the MX records of backup.com (where backup.com is a domain that acts as a backup MX server for your domain).
  • Install the SPF checking tool in Postfix
root@server:~# aptitude install postfix-policyd-spf-python
  • Add the SPF checker to /etc/postfix/master.cf
policy-spf  unix  -       n       n       -       -       spawn
    user=nobody argv=/usr/bin/policyd-spf
  • Add the SPF timeout line, and modify smtpd_relay_restrictions in /etc/postfix/main.cf
smtpd_relay_restrictions =  permit_sasl_authenticated
                            permit_mynetworks
                            reject_unauth_destination
                            check_policy_service unix:private/policy-spf

policy-spf_time_limit = 3600s
  • Modify /etc/postfix-policyd-spf-python/policyd-spf.conf to whitelist any mail servers that act as secondary for this domain. Add this line:
Domain_Whitelist = mybackup.domain.com
  • Test the setup by sending a message from a domain that uses SPF to the server you control. The mail log for your MTA should contain a line like
Nov 20 10:26:05 server policyd-spf[18300]: Pass; identity=helo; client-ip=157.56.112.83; helo=blah.senderdomain.com; envelope-from=user@senderdomain.com; receiver=user@domain.com 
and the headers of the message should contain a line like
Received-SPF: Pass (sender SPF authorized) identity=helo; client-ip=x.x.x.x; helo=blah.senderdomain.com; envelope-from=user@senderdomain.com; receiver=user@domain.com
  • Test the setup by sending a message to a domain that uses SPF from your own server. The headers of that message should contain a line like
Received-SPF: Pass 
followed by a bunch of stuff that shows how the receiving MTA checks SPF records.

(Taken from Ubuntu community docs for Postfix/SPF).

DKIM

With DKIM, the originating MTA takes a hash of some message headers and the message body, signs it with the server’s private key, and embeds the signed hash as an additional header in the message. The public key is published as a DNS TXT record. The receiving MTA recalculates the hash and compares it to the signed version in the message. If they match, the message passes the DKIM authentication.

In this example, I have several domains that can send mail. Each has a separate DKIM key. The details are stored in the /etc/opendkim/key_table and /etc/opendkim/signing_table files, rather than in the /etc/opendkim.conf file (the latter is fine if you only have one domain).

  • Install the packages
root@server:~# aptitude install opendkim opendkim-tools
  • Edit /etc/opendkim.conf
# Log to syslog
Syslog                  yes
# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
UMask                   002

# Sign for example.com with key in /etc/mail/dkim.key using
# selector '2007' (e.g. 2007._domainkey.example.com)
#Domain                  example.com
#KeyFile                 /etc/mail/dkim.key
#Selector                mail
KeyTable                /etc/opendkim/key_table
SigningTable            /etc/opendkim/signing_table
ExternalIgnoreList      /etc/opendkim/trusted_hosts
InternalHosts           /etc/opendkim/trusted_hosts

# Commonly-used options; the commented-out versions show the defaults.
#Canonicalization       simple
#Mode                   sv
#SubDomains             no
#ADSPAction            continue

AutoRestart             yes
AutoRestartRate         10/1h
Background              yes
UserID                  opendkim:opendkim

Canonicalization        relaxed/relaxed
DNSTimeout              5
Mode                    sv
SignatureAlgorithm      rsa-sha256
SubDomains              no
#UseASPDiscard          no
#Version                rfc4871
X-Header                no

  • Modify /etc/defaults/opendkim to include the single SOCKET line at the end.
# Command-line options specified here will override the contents of
# /etc/opendkim.conf. See opendkim(8) for a complete list of options.
#DAEMON_OPTS=""
#
# Uncomment to specify an alternate socket
# Note that setting this will override any Socket value in opendkim.conf
#SOCKET="local:/var/run/opendkim/opendkim.sock" # default
#SOCKET="inet:54321" # listen on all interfaces on port 54321
#SOCKET="inet:12345@localhost" # listen on loopback on port 12345
#SOCKET="inet:12345@192.0.2.1" # listen on 192.0.2.1 on port 12345

SOCKET="inet:8891@localhost" # listen on loopback on port 8891
  • Create the directories for the keys (one for each domain)
mkdir /etc/opendkim
mkdir /etc/opendkim/example.com
mkdir /etc/opendkim/example2.com 
  • Create /etc/opendkim/trusted_hosts to show which hosts don’t need DKIM authentication. In this case, it’s just the hosts my LAN.
# local host
127.0.0.1
# local subnets that are trusted and do not need to be verified
192.168.1.0/24
  • Create the keys for each domain
cd /etc/opendkim/example.com
opendkim-genkey -s mail -d example.com
chown opendkim:opendkim mail.private
  • Create /etc/opendkim/signing_table
example.com      mail._domainkey.example.com
example1.com     mail._domainkey.example1.com
example2.com     mail._domainkey.example2.com
  • Place the keys in your domains’ DNS records. Create a new subdomain mail._domainkey for each domain (e.g. mail._domainkey.example.com). Create a new TXT record for that new subdomain that contains the core of the relevant part of/etc/opendkim/example.com/mail.txt. If the file contains
mail._domainkey IN      TXT     ( "v=DKIM1; k=rsa; "  "p=MIGfMA0GCSqGSIb3DQEBAQU...blah...blah...QIDAQAB" )  ; ----- DKIM key mail for example.com

the DNS TXT record for mail._domainkey.example.com should contain

v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQU...blah...blah...QIDAQAB
  • Check the entry is in the domain
user@desktop:~$ host -t TXT mail._domainkey.example.com ns1.isp.net
Using domain server:
Name: ns1.isp.net
Address: 100.100.100.1006#53
Aliases: 

mail._domainkey.example.com descriptive text "v=DKIM1\; k=rsa\; p=MIGfMA0GCSqGSIb3DQEB...blah...blah...uY/wIDAQAB"
  • Modify /etc/postfix/main.cf to include
# DKIM Milter
milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
(If you already have milters operating, add the DKIM milter settings to the existing ones
milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891,inet:localhost:8892
non_smtpd_milters = inet:localhost:8891,inet:localhost:8892
  • Modify /etc/postfix/master.cf to prevent messages being signed when going both to and from Amavis. In the section for Amavis, extend the receive_override_options with no_milters:
# Returning from Amavis
127.0.0.1:10025 inet    n       -       -       -       -       smtpd
 -o content_filter=
...
 -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
  • Restart everything, checking /var/log/mail.log for errors
root@server:~# service opendkim restart
root@server:~# postfix reload
  • Test it all works by sending some messages to and from domains with working DKIM services. Outgoing messages should contain a header like
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=mail;
       t=1448108655; bh=Hciu/taJLWKuURd9G8XyD6VK2YpDwXAHXAfUrZpPfRo=;
       h=Date:Subject:From:To:From;
       b=IFbjcnrZcD0SQsL9N2zGaOjhR/j2opmjAmxBgp8+3QejtqPgbNqFjdUOeXkQlh0xo
        M1nX2VK73v+2D7Og41jZuXQgb8jYn2yUeKLkvJDWNnEOdpmXvRpspuhu2+uqwEk8cj
        uqkY5jTXUBmn9FKVRRn0Xg8Nzl0jG7HY9pYNPXYY=
If it works, the receiving MTA should also include a header like
Authentication-Results: spf=pass (sender IP is 100.100.100.100)
 smtp.mailfrom=example.com; desitination.com; dkim=pass (signature was verified)
 header.d=example.com;destination.com; dmarc=bestguesspass action=none
 header.from=example.com;
Check that the DKIM process works in both directions and for each domain you have.
  • Once you’re confident that DKIM is working, tell the world to reject messages that don’t have the DKIM keys. Create a new domain adsp._domainkey.example.com and give it a DNS TXT record containing
dkim=all

Most of these instructions were taken from How to eliminate spam and protect your name with DMARC, with some from Postfix/DKIM Ubuntu community documentation.

DMARC

DMARC is a method for MTAs to publish a policy for how their messages should be processed, and allows them to report how well they’re handling each others’ mail according to those policies.

DMARC policy for outgoing mail

You create a DNS TXT record that tells other MTAs about how you want your outgoing messages to be processed. You set up a DMARC milter on your MTA to process incoming messages according to the published DMARC policy. The DMARC milter also logs how you’re handling other MTAs’ messages and can send reports to them as requested..

  • Create a subdomain _dmarc.example.com and a TXT record specifying the DMARC reports you want.
v=DMARC1; p=none; rua=mailto:dmarc@example.com; fo=1; adkim=r; aspf=r
(The KTS DMARC Record Assistant will create the records for you. Remove the fo=1; for fewer reports once you’re confident things are working.)
  • Create the dmarc user by modifying /etc/postfix/valiases to include lines
dmarc@example.com               user@example1.com
dmarc@example1.com              user@example1.com
dmarc@example1.com              user@example1.com
  • Refresh the Postfix config.
root@server:~# postmap /etc/postfix/valiases
root@server:~# postfix reload

You should get messages arriving from other MTAs to say how well you’ve been doing.

Testing and logging incoming mail

Next is to log the details from incoming messages and tell those MTAs how well they’ve been doing.

  • Install OpenDMARC
root@server:~# aptitude install opendmarc
  • Update /etc/opendmarc.conf
AuthservID mail.domain1.com
PidFile /var/run/opendmarc.pid #Debian default
Socket inet:8893@localhost
RejectFailures false
Syslog true
TrustedAuthservIDs mail.domain1.com,mail.domain2.com,mail2.example.com
UMask 0002
UserID opendmarc:opendmarc
IgnoreHosts /etc/opendmarc/ignore.hosts
HistoryFile /var/run/opendmarc/opendmarc.dat
#for testing:
SoftwareHeader true
where mail.domain1.com,mail.domain2.com,mail2.example.com are the names of mail servers you trust, including your own. I included the names of the other domains this MTA serves.
  • Create the directory /etc/opendmarc/<code> and the file <code>/etc/opendmarc/ignore.hosts, containing
localhost
192.168.1.0/24
  • Update /etc/default/opendmarc to include the line
SOCKET="inet:8893@localhost"
  • Exit /etc/postfix/main.cf to include the extra milter. Add the port specification to these lines:
smtpd_milters = inet:localhost:8891,inet:localhost:8893
non_smtpd_milters = inet:localhost:8891,inet:localhost:8893
  • Start OpenDMARC and refresh Postfix
root@server:~# service opendmarc restart
root@server:~# postfix reload

Send a mail to yourself from a service that uses DMARC, such as GMail. Messages you receive should include headers like:

DMARC-Filter: OpenDMARC Filter v1.2.0 mail.domain.org 821E7A4
Authentication-Results: mail.domain.org; dmarc=pass header.from=gmail.com
The first line is the flag from the SoftwareHeader line, and shows that OpenDMARC handled the message. The second line is the DMARC authentication result. If it’s all working, you can remove the SoftwareHeader line from/etc/opendmarc.conf and restart OpenDMARC.

Reporting on incoming mail

Important note on versions
The instructions below won’t generate any messages with Ubuntu 14.04 LTS. The supplied version of OpenDMARC in that version of Ubuntu (1.2.0) has a bug where it fails to record the DMARC rua address, which specifies where the DMARC reports should go. This is fixed in OpenDMARC 1.3.1, which is bundled in Ubuntu 15.04 and later.

The final part is to log the results of DMARC testing incoming mail, and sending the results back to the originating MTAs.

  • Edit /usr/share/doc/opendmarc/schema.mysql to uncomment the lines
CREATE USER 'opendmarc'@'localhost' IDENTIFIED BY 'changeme';
GRANT ALL ON opendmarc.* to 'opendmarc'@'localhost';
create a stong password to replace ‘changeme’.
  • Run the script to create the MySQL database to store the reports.
root@server:~# mysql -u root -p < /usr/share/doc/opendmarc/schema.sql
  • Fix the missing database backend selectors in the OpenDMARC scripts. In /usr/sbin/opendmarc-import , /usr/sbin/opendmarc-params , and /usr/sbin/opendmarc-reports , change the DBD lines to use “mysql” not “yes” (you may have to hunt, and it seems already fixed in /usr/sbin/opendmarc-expire),
# require DBD::yes;
require DBD::mysql;

# my $dbscheme     = "yes";
my $dbscheme     = "mysql";
  • Create /etc/cron.daily/opendmarc-report to generate the reports once a day.
#!/bin/bash
 
DB_SERVER='localhost'
DB_USER='opendmarc'
DB_PASS='changeme'
DB_NAME='opendmarc'
WORK_DIR='/var/run/opendmarc'
REPORT_EMAIL='dmarc@domain.com'
REPORT_ORG='domain.com'
 
mv ${WORK_DIR}/opendmarc.dat ${WORK_DIR}/opendmarc_import.dat -f
touch ${WORK_DIR}/opendmarc.dat
chown opendmarc:opendmarc ${WORK_DIR}/opendmarc.dat
/usr/sbin/opendmarc-import --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose < ${WORK_DIR}/opendmarc_import.dat
/usr/sbin/opendmarc-reports --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose \
    --interval=86400 --report-email=${REPORT_EMAIL} --report-org=${REPORT_ORG}
/usr/sbin/opendmarc-expire --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose
where you use the DB_PASS password you generated earlier.
  • Make the cron script executable:
root@server:~# chmod +x /etc/cron.daily/opendmarc-report
  • Ensure that, for testing at least, you get a copy of every outgoing DMARC report. Modify /etc/postfix/main.cf to include
sender_bcc_maps = hash:/etc/postfix/sender_bcc
then create /etc/postfix/sender_bcc to contain the one line
/dmarc@domain1.com/ user@domain1.com
and refresh the Postfix configuration
root@server:~# postmap /etc/postfix/sender_bcc
root@server:~# postfix reload

Most of these instructions were taken from How to eliminate spam and protect your name with DMARC, with some from Christian Laußat’s Linux Blog.

DANE

To add: see documents at http://www.postfix.org/TLS_README.html#client_tls_dane . Will require DNSSEC enabled on the sending domain.

See also

Here are a few pages that are useful guides or provide background and context.