How to Setup Postfix Mail Server with DKIM

Postfix DKIMInstallation and configuration of the mail server (Mail Transfer Agent, MTA) is one of the typical tasks any system administrator faces. Here we will cover the task and by the end of the article will have CentOS 7 with Postfix mail server that listens 25 port (SMTP) and deliver messages for the selected domain to users mail boxes.

First, we need to have a domain’s DNS records properly configured. This is done in 2 places:

A, TXT and MX records are stored at your domain’s registrar, GoDaddy for example.

To check the record using the ‘dig’ command with the record name and domain name as the argument. In our example, my domain andreybondarenko.com is configured as follows:

  ┌[root@andreybondarenko]─[~]
  └─10/09 08:37:08-# dig mx andreybondarenko.com
  [...]
  ;; ANSWER SECTION:
  andreybondarenko.com. 1800 IN MX 1 andreybondarenko.com.
  [...]

That indicates that the mail for the entire andreybondarenko.com domain is served by the host named ‘andreybondarenko.com’ because I have only one host. If we have more, we can create mx1.andreybondarenko.com and mx2.andreybondarenko.com and they would deliver mail randomly chosen by the client. This is useful for stability, redundancy and load balance.

PTR record is set at the hosting provider side. You can either find the record in the control panel or as technical support service will do this for you.If the hosting provider will refuse to change it, your messages would be rejected by most large mail servers (including Google). This record is called ‘reverse’ and allows anyone to check that the IP address that is used to initiate connection has a valid domain name. It’s a fast and easy way to detect spam bot: it use IP address it does not control, so it cannot set the PTR record properly.

Second, the Postfix itself should be installed. I recommend to use it as a mail server due to its highly flexible, secure, extensible and can provide you high performance on large installations. On the CentOS just run:

yum install postfix

And the packet manager will install it with all dependencies needed. At the moment the version 2.10.1 is the latest available in the CentOS repository, so I will use it.
Postfix has 2 configuration files. /etc/postfix/master.cf controls process count, their behavior, should they use chrooted environment or not for and the defaults are OK for most cases. The /etc/postfix/main.cf controls everything else, we need to change following:

  myhostname = andreybondrenko.com
  mydomain = andreybondarenko.com

This two variables should be set to the valid host name of the host itself and to the domain he is serving mail for. In my case they are same.

  myorigin = $myhostname

The myorigin parameter specifies the domain that locally-posted mail appears to come from. The default is to append $myhostname, which is OK for small sites. If you run a domain with multiple mail servers, you should change this to $mydomain and set up a domain-wide alias database that aliases each user user@users.host. To maintain consistency between sender and recipient addresses, myorigin also specifies the default domain name that is appended to recipient addresses that have no @domain part. We will use the default setting.

  inet_interfaces = all
  inet_protocols = all

This two settings will tell the smtpd to listen both IPv4 and IPv6 on all interfaces.

  mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, myotherdomainihave.com

This variable sets all domains we would accept mail to. In this case, that’s andreybondarenko.com, myotherdomainihave.com, and localhost.

relay_domains = $mydestination
This variable sets all domains for we would relay mail in case the recipient is not located on our host.
That is basically all the initial configuration we need: it tells the smtpd to listen to 25 port on all interfaces, accept mail to andreybondarenko.com, localhost and myotherdomainihave.com. By default, it would deliver mail to users mailboxes in the /var/mail, in future, we can set up dovecot to be an IMAP server and provide the SASL authentication, then we can harden the Postfix against spam, setup greylisting and DKIM. The last thing we need to do is to tell Systemd to start Postfix on boot and to run it now:

  systemctl enable postfix
  systemctl start postfix

We can now see it’s running in process list:

   ┌[root@andreybondarenko]─[~]
   └─10/09 09:16:44-# ps ax|grep postfix
   1764 ? Ss 0:02 /usr/libexec/postfix/master -w

We can connect to the 25 port and send a message with the telnet:

  ┌[root@andreybondarenko]─[~]
  └─10/09 09:19:13-# telnet localhost 25
  Trying ::1...
  Connected to localhost.
  Escape character is '^]'.
  220 andreybondrenko.com ESMTP Postfix
  helo andreybondarenko.com
  250 andreybondrenko.com
  mail from:
  250 2.1.0 Ok
  rcpt to:
  250 2.1.5 Ok
  data
  354 End data with .
  test
  .
  250 2.0.0 Ok: queued as EBA0881589
  quit
  221 2.0.0 Bye

  Connection closed by foreign host.

And see it in the /var/log/maillog

  Sep 10 09:19:18 andreybondarenko postfix/smtpd[10889]: connect from localhost[::1]
  Sep 10 09:19:47 andreybondarenko postfix/smtpd[10889]: EBA0881589: client=localhost[::1]
  Sep 10 09:19:54 andreybondarenko postfix/cleanup[10895]: EBA0881589: message-id=
  Sep 10 09:19:54 andreybondarenko postfix/qmgr[1770]: EBA0881589: from=, size=352, nrcpt=1

Now it’s time to set up DKIM (see https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail). In fact, the only thing that Domain Key technology provides is the information that the person who controls the DNS also controls the mail server. This means we should set up two places at the same time: the DNS TXT record and special daemon on the server side.

First, install the opendkim package:

  yum install opendkim

Second, run

┌[root@andreybondarenko]─[~]
└─10/09 18:46:11-# opendkim-default-keygen
Generating default DKIM keys:
Default DKIM keys for freshdaymall.com created in /etc/opendkim/keys.

In the /etc/opendkim/keys there would be 2 files containing public and private keys. Check those permissions are following for the private key (only opendkim user can read):

  ┌[root@andreybondarenko]─[/etc/opendkim/keys]
  └─10/09 18:50:52-# ll default.private
  -r--------. 1 opendkim opendkim 887 Oct 2 2016 default.private

Now we need to correct settings.

  • /etc/opendkim.conf - configuration file of opendkim
  • /etc/opendkim/keytable — defines the path of the private key for the domain
  • /etc/opendkim/signingtable - tells OpenDKIM how to apply the keys.
  • /etc/opendkim/TrustedHosts - defines which hosts are allowed to use keys.

I have copied my public and private keys to the /etc/opendkim

  ┌[root@andreybondarenko]─[/etc/opendkim]
  └─10/09 18:53:40-# ls -la
  total 48K
  drwxr-xr-x. 3 root opendkim 4.0K Jan 9 2017 .
  drwxr-xr-x. 96 root root 12K Sep 8 07:28 ..
  -r--------. 1 opendkim opendkim 887 Oct 2 2016 andreybondarenko.com.private
  -rw-r-----. 1 root opendkim 333 Oct 2 2016 andreybondarenko.com.txt
  drwxr-x---. 2 opendkim opendkim 4.0K Dec 21 2016 keys
  -rw-r--r--. 1 root root 106 Oct 3 2016 keytable
  -rw-r--r--. 1 root root 60 Oct 3 2016 signingtable
  -rw-r-----. 1 opendkim opendkim 378 Dec 21 2016 TrustedHosts

In the /etc/opendkim.conf we need following parameters:

  • Mode sv - sign and verify outgoing messages
  • Socket inet:8891@localhost - use port 8891 at localhost for communication
  • ReportAddress "andreybondarenko.com Postmaster" - postmaster’s address.
  • Domain andreybondarenko.com - my domain
  • Selector mail - the part to look at the TXT record for the key.

Other parameters could be lived by default.

Then setting /etc/opendkim/keytable as follows:

  mail._domainkey.andreybondarenko.com andreybondarenko.com:mail:/etc/opendkim/andreybondarenko.com.private

And the /etc/opendkim/signingtable

  *@andreybondarenko.com mail._domainkey.andreybondarenko.com

The first file defines where to look for the private key for the domain (remember that real life server can have many of them) and second defines that all mail should be signed with this key. TrustedHosts is trivial and default:

  127.0.0.1
  ::1

Now we can run opendkim:

  systemctl enable opendkim
  systemctl start opendkim

Last part at the host is to tell the Postfix to forward all mail to the opendkim first by adding local milter :

  smtpd_milters = inet:127.0.0.1:8891
  non_smtpd_milters = inet:127.0.0.1:8891

That’s it! Restart the Postfix to get new config:

  systemctl restart postfix

Now the last and very important part: we need to the public the open part of the key to the world with the DNS TXT record. You need to create the record as follows:

  • Name - mail._domainkey
  • Type: TXT
  • TTL: 1 hour
  • Value: v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvGRWfWPPZVIg0fy7Pr0+rsBsoL6Imt1GBE/QRd3X5Izv1iAJFUsOteaf9TI9EO/YFwoLLahzuoZM1aUU4ED3fHlItEnqXCKQhX8Zripi7cfIO+DRFEhGuQtG6OIuA6+c3ivao7DTPk/IFqY7MG5M3wMvAfV+eIBf1VjmajSwe3wIDAQABLast part, the ‘value’ is tricky and may lead to mistakes. You need to read the /etc/opendkim/your_domain.txt or /etc/opendkim/keys/default.txt and copy the value removing all quote symbols (i.e. all “).

The mail server is common yet complicated service since it interacts with the outer world and must obey best practices, so the mail would be accepted and never lost.

About Andrey Bondarenko

I am Linux geek with about 20 years of experience in security, mail server, identity management.

Author Archive Page

Have anything to say?

Your email address will not be published. Required fields are marked *

All comments are subject to moderation.