Configuring postfix to prevent your emails from being flagged as SPAM

This post describes how to properly defines postfix server properties to prevent your emails from being falsely flagged as spams or even rejected (especially by Google). It lists the different policies and tools that have to be checked to ensure a maximum probability to pass all filters. This post is dedicated to my old buddy Stéphanie, whose email (gmail) address often rejected (maybe not wrongly) my emails.
As a use case example, we will consider that the user (me) wants to send email from the addresses tuxette@mydomain.org and samantha@myotherdomain.wf via the mail server with name mail.mydomain.org. I own the two domain names mydomain.org and myotherdomain.wf. mail.mydomain.org has for IP XX.YYY.ZZZ.WW which reverses to the server name chix.mydomain.org. In addition, my server only sends email via IPV4 (option inet_protocols = ipv4 in the file /etc/postfix/main.cf to avoid painful headaches in configuring properly DNS and PTR for IPV6).

MX, PTR and reverse

All these records must be set properly (in my case, they are managed through the OVH user interface because my domain names are rented by OVH:

  • MX records for myotherdomain.wf and mydomain.org points to mail.mydomain.org which means that all emails sent to @myotherdomain.wf and @mydomain.org are directed to the mail server mail.mydomain.org. This rule writes

    IN MX 1 mail.mydomain.org.
    		
  • DNS and PTR records must also be properly set: the DNS record associates the IP XX.YYY.ZZZ.WW to the mail servermail.mydomain.org. This rule is restricted to the domain name mydomain.org and writes

    mail IN A XX.YYY.ZZZ.WW
    		

    Also, a PTR has to be set for the IP address XX.YYY.ZZZ.WW. This PTR record associates a domain name to the IP, in my case chix.mydomain.org (hence, PRT does not necessarily points to the FQDN of the mail server but chix.mydomain.org must have a PTR record which matches the IP XX.YYY.ZZZ.WW).

All these records (as well as IP blacklisting) can be checked at this link. In particular:

  • MX Lookup should return mydomain.org when queried on mydomain.org or myotherdomain.wf;
  • Blacklisting Check should be OK for XX.YYY.ZZZ.WW;
  • Blacklisting Check should be OK for XX.YYY.ZZZ.WW;
  • DNS Lookup for mail.mydomain.org and chix.mydomain.org should return XX.YYY.ZZZ.WW;
  • Reverse Lookup (PTR) for XX.YYY.ZZZ.WW should return chix.mydomain.org;
  • finally, both mydomain.org or myotherdomain.wf can be checked for “Test Email Server” to find any anomaly in mail server configuration.

SPF

A SPF (Sender Policy Framework) record tells which servers/IP … are allowed to send emails for a given domain name. If no SPF is defined and an email is sent to a gmail address, you should see the following record in the headers:

Received-SPF: neutral (google.com: XX.YYY.ZZZ.WW is neither permitted nor denied
  by best guess record for domain of samantha@myotherdomain.wf) 
  client-ip=XX.YYY.ZZZ.WW;

This framework can be used to tell the receiver’s email server that the email has actually been sent by the expected mail server. In my case, I created a SPF record for both mydomain.org and myotherdomain.wf with the same policy which tells that the IP of mydomain.org and myotherdomain.wf are allowed to send emails as well as the MX of mydomain.org and myotherdomain.wf. Finally, I also explicitely authorized the IP XX.YYY.ZZZ.WW to send emails. This record writes

600 IN TXT "v=spf1 a mx ip4:XX.YYY.ZZZ.WW ~all"

in which the ~all tells that this is a soft policy (maybe emails can be sent by other servers although it is not expected). OVH interface allows to configure in a very intuitive manner SPF.

Sending an email to a gmail account, the received header should now look like:

Received-SPF: pass (google.com: domain of samantha@myotherdomain.wf designates 
  XX.YYY.ZZZ.WW as permitted sender) client-ip=XX.YYY.ZZZ.WW;

More details about SPF at this link (in French). SPF records can be checked at this link.

DKIM

DKIM enables a domain name to be associated with an email message and thus to claim for responsibility for an email. DKIM is powered by asymmetric cryptography: a key is included in every email header which should match your DKIM record (TXT field corresponding to your domain name) in order to pass the DKIM filter. I installed it using the tutorial available at this link. First, opendkim tools have to be installed:

sudo apt-get install opendkim opendkim-tools

which configuration file /etc/opendkim.conf is edited to add

AutoRestart             Yes
AutoRestartRate         10/1h
UMask                   002
Syslog                  yes
SyslogSuccess           Yes
LogWhy                  Yes

Canonicalization        relaxed/simple

ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable

Mode                    sv
PidFile                 /var/run/opendkim/opendkim.pid
SignatureAlgorithm      rsa-sha256

UserID                  opendkim:opendkim

Socket                  inet:12301@localhost

Then, /etc/default/opendkim is editing and the following line is added:

SOCKET="inet:12301@localhost"

To connect opendkim to postfix, edit the file /etc/postfix/main.cf and add the following two lines:

milter_protocol = 2
milter_default_action = accept

Create a file /etc/opendkim/TrustedHosts with

127.0.0.1
localhost
192.168.0.1/24

*.mydomain.org
*.myotherdomain.wf

and a file /etc/opendkim/KeyTable with

mail._domainkey.mydomain.org mydomain.org:mail:/etc/opendkim/keys/mydomain.org/mail.private
mail._domainkey.myotherdomain.wf myotherdomain.wf:mail:/etc/opendkim/keys/myotherdomain.wf/mail.private

and also a file /etc/opendkim/SigningTable
with

*@mydomain.org mail._domainkey.mydomain.org
*@myotherdomain.wf mail._domainkey.myotherdomain.wf

Finally, to generate the keys, create the following directories in /etc/opendkim : keys/mydomain.org keys/myotherdomain.wf and run

sudo opendkim-genkey -s mail -d mydomain.org
sudo chown opendkim:opendkim mail.private

inside /etc/opendkim/keys/mydomain.org and

sudo opendkim-genkey -s mail -d myotherdomain.wf
sudo chown opendkim:opendkim mail.private

inside /etc/opendkim/keys/myotherdomain.wf.

A DKIM record is then added to each of the two domain name:

mail._domainkey IN TXT "v=DKIM1; k=rsa; p=WWWWWWWWWWW"

in which WWWWWWWWWWW has to be replaced by the (long) text key available in the file /etc/opendkim/keys/mydomain.org/mail.txt (and similarly for the record DKIM record for clementine.wf). Then, an email received by gmail includes the following text in the headers:

Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of samantha@myotherdomain.wf designates 
         XX.YYY.ZZZ.WW as permitted sender) 
         smtp.mailfrom=samantha@myotherdomain.wf;
       dkim=pass header.i=@samantha@myotherdomain.wf;

which tells that both DKIM and SPF filters have been successfully passed. Again, DKIM records can be checked at this link.

DMARC

Finally, SPF and DKIM can be monitored using a DMARC record. DMARC, which stands for “Domain-based Message Authentication, Reporting & Conformance”, standardizes how email receivers perform email authentication using the well-known SPF and DKIM mechanisms. This means that senders will experience consistent authentication results for their messages.

A DMARC rule can be recorded in a TXT field of your domain name (so, in the present use case for mydomain.org and myotherdomain.wf. A basic overview of how DMARC rules are created and work is available at this link. In my case, I created a DMARC record which takes no policy for domain mismatch, subject 10% of the messages to filter (this rule seems to be overridden by google anyway) and relaxed policy for alignment of SPF and DKIM records. Daily reports will be received at tuxette@mydomain.org :

_dmarc IN TXT "v=DMARC1;p=none;sp=reject;pct=10;adkim=r;aspf=r;fo=1;
  ri=86400;rua=mailto:tuxette@mydomain.org"

The reports can be checked and better understood using the tool available at this link. A proper DMARC record should lead to headers similar to:

Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of samantha@myotherdomain.wf designates 
         XX.YYY.ZZZ.WW as permitted sender) 
         smtp.mailfrom=samantha@myotherdomain.wf;
       dkim=pass header.i=@samantha@myotherdomain.wf;
       dmarc=pass (p=NONE dis=NONE) header.from=samantha@myotherdomain.wf

DMARC records can be checked at this link.