This page looks best with JavaScript enabled

Mail system with Postfix-Dovecot-MySQL-OpenDKIM

 ·  🎃 kr0m

In this occasion, I bring you top-notch material. We will set up a mail system with which the recipient of the email can ensure that the origin of the email is us and not someone else impersonating our identity.

To do this, we will use SPF and DKIM:

  • SPF: TXT entry in the DNS zone of the domain that is being used to send emails where the IP addresses of the SMTP servers of the domain are indicated.
  • DKIM: Cryptographic signature published in a TXT record of the domain, signing with the public key on the server, the recipient will be able to verify that the email is ours by reading the public key from the DNS.
  • MX: DNS entry that indicates which equipment the incoming mail should be sent to for that domain.

NOTE: The signature consists of encrypting with the server’s private key the MD5 of the headers and body of the email. The recipient, using the public key published in the DNS, can ensure that this data has not been altered. As the MD5 is completely reliable, it can be calculated locally. If the received and calculated values match, the email is correct. For this to fail, the DNS server would have to be hacked, altering the public key of the domain.

For sending emails, we have opted for Postfix for its speed. For accessing emails, we have opted for Dovecot for being focused on security from the ground up. Both systems will use a MySQL database for authentication, but taking into account that Postfix will use DovecotSASL as an authentication backend, that is, Postfix will not directly access the database, but will make the request through SASL to Dovecot, and it will consult the DB.

We compile Postfix with support for MySQL and DovecotSASL:

vi /etc/portage/package.use/postfix

=mail-mta/postfix-2.10.2 mysql dovecot-sasl
emerge -av mail-mta/postfix
vi /etc/portage/package.use/dovecot
=net-mail/dovecot-2.2.5 mysql
emerge -av dovecot

We configure Dovecot:

emerge –config dovecot
chmod 600 /etc/dovecot/dovecot.conf

IMAP connections will be encrypted with a self-signed certificate, so we must generate the certificate request and sign it ourselves:

cd /etc/ssl/dovecot/
openssl genrsa -out 2048
openssl req -new -key -out
openssl x509 -req -days 3650 -in -signkey -out
cat »
cat »
chmod 400*

I personally prefer the IMAP protocol, so I disable the rest:

vi /etc/dovecot/dovecot.conf

#protocols = imap pop3 lmtp
protocols = imap
listen = *
dict {
  #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext
!include conf.d/*.conf
!include_try local.conf

disable_plaintext_auth = yes
ssl = yes
ssl_cert =</etc/ssl/dovecot/
ssl_key =</etc/ssl/dovecot/
mail_location = maildir:~/.maildir
vi /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_cert = </etc/ssl/dovecot/
ssl_key = </etc/ssl/dovecot/

NOTE: ntpclient does not work well in combination with Dovecot, it is preferable to use ntpd and make sure that the dovecot script requires ntpd before starting.

emerge -av ntp
vi /etc/init.d/dovecot
depend() {
        need localmount ntpd

We configure the MySQL user database.

If it is a new installation, we assign a password (be careful with strange characters):

openssl rand -base64 12
emerge -av mysql
emerge –config mysql

We create the database and table:

CREATE database postfix CHARACTER SET utf8 COLLATE utf8_general_ci;
USE postfix;

We create the database access user:

use mysql GRANT ALL PRIVILEGES ON postfix.* TO postfix@localhost IDENTIFIED BY 'XXXXXXX';

We configure the way Dovecot should query the DB:

vi /etc/dovecot/dovecot-sql.conf.ext

driver = mysql
connect = dbname=postfix user=postfix host=localhost password=XXXXXXX
default_pass_scheme = PLAIN
password_query = SELECT password FROM users WHERE username = '%u'
user_query = SELECT home, uid, gid FROM users WHERE username = '%u'
vi /etc/dovecot/conf.d/10-master.conf
service imap-login {
  inet_listener imap {
    #port = 143
  inet_listener imaps {
    #port = 993
    #ssl = yes

service pop3-login {
  inet_listener pop3 {
    #port = 110
  inet_listener pop3s {
    #port = 995
    #ssl = yes

service lmtp {
  unix_listener lmtp {
    #mode = 0666

service auth {
  unix_listener auth-userdb {

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

service auth-worker {

service dict {
  unix_listener dict {

We enable auto-creation of directories:

vi /etc/dovecot/conf.d/20-imap.conf

protocol imap {
mail_plugins = $mail_plugins autocreate

plugin {
autocreate = Trash
autocreate2 = Junk
autocreate3 = Drafts
autocreate4 = Sent
autosubscribe = Trash
autosubscribe2 = Junk
autosubscribe3 = Drafts
autosubscribe4 = Sent

We empty the auth-system config:

> /etc/dovecot/conf.d/auth-system.conf.ext

useradd -m -G users,wheel -s /bin/bash user00
grep user00 /etc/passwd –> UID
grep user00 /etc/group –> GID

mysql -u root -p
use postfix
INSERT INTO users (username, domain, password, home, uid, gid, active) VALUES ('', '', 'XXXXXXXX', '/home/user00', 'UID', 'GID', 'Y');

We generate the self-signed certificate for Postfix:

cd /etc/ssl/postfix
openssl genrsa -out 2048
openssl req -new -key -out
openssl x509 -req -days 3650 -in -signkey -out
cat »
cat »
chmod 400*

We configure Postfix to use Dovecot as an authenticator through SASL, Dovecot will connect to the MySQL database:

vi /etc/postfix/

queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix
mail_owner = postfix
myhostname =
mydomain =
inet_interfaces = all
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
unknown_local_recipient_reject_code = 550
mynetworks =
home_mailbox = .maildir/
debug_peer_level = 2
debugger_command =
         ddd $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
html_directory = no
manpage_directory = /usr/share/man
sample_directory = /etc/postfix
readme_directory = no
inet_protocols = ipv4
local_destination_concurrency_limit = 2
default_destination_concurrency_limit = 10

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
local_header_rewrite_clients = permit_mynetworks
broken_sasl_auth_clients = yes
smtpd_authorized_xforward_hosts = $mynetworks

# Parametro de filtrado relay en postfix >=2.10
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
#smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

# TLS stuff - Enable if you want to use SMTP over SSL
smtpd_use_tls = yes
smtpd_tls_key_file = /etc/ssl/postfix/
smtpd_tls_cert_file = /etc/ssl/postfix/
#mtpd_tls_CAfile = /etc/ssl/postfix/cacert.pem
smtpd_tls_loglevel = 0
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
local_recipient_maps =
alias_maps = hash:/etc/mail/aliases

# INFGb de adjuntos:
message_size_limit = 0
mailbox_size_limit = 0

We put postfix in verbose mode for testing:

vi /etc/postfix/

smtp      inet  n       -       n       -       -       smtpd -v

We compile the SASL libraries:

emerge -av dev-libs/cyrus-sasl
/etc/init.d/saslauthd start
rc-update add saslauthd default

We add an alias for the root user:

vi /etc/mail/aliases

postmap /etc/mail/aliases
/etc/init.d/postfix restart

To allow mail servers to deliver mail to our PostFix, we need to configure an MX entry with the PostFix IP. Depending on whether we have our own DNS server or rely on an external one, we will either edit the zone directly or use the ISP’s web panel. To check that the configuration is correct:

dig @ MX 21600 IN MX 1
dig @ 21600 IN A A.B.C.D

NOTE: If we do not configure the DNS entries correctly and perform a query, it will be cached in the DNS servers to which we made the request. Even if we correct the error, the DNS will not propagate until the cache expires. In the previous example, the cache is 21600s.

We add all configured services to the default runlevel:

rc-update add mysql default
rc-update add postfix default
rc-update add dovecot default
rc-update add ntpd default

At this point, we can already send and receive email. In Thunderbird, for example, the configuration would be:

STARTTLS -- Normal password

Now we will focus on the OpenDKIM configuration:

emerge -av opendkim

NOTE: The opendkim-genkey from the repositories seems to generate DNS entries that are a bit strange.

The version used:

opendkim-genkey v2.7.4

So I will use this script that I know works correctly. If you don’t trust my script (you should never trust anyone), you can always use the newly installed one, which should work correctly, or review the source code to make sure it doesn’t do anything strange.

We generate the public and private key:

cd /root
./opendkim-genkey -s smtp -d
cp smtp.* /etc/opendkim/

The content of smtp.txt should be something like this:

smtp._domainkey IN TXT "v=DKIM1; r=postmaster; g=*; k=rsa;
p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADdDSs33fds3s42sDS3DSFEG54GHJHJKL9WeUF2QK4o+HC9kNkN3M3LdXdCQxtDBb5afEOrk9wQjSqJre6v2sa4fGGG4343f0fsdfsdf5FAynjoVza8oSV5qXeGmqUGetEUQZV4qSO0oE5IOmLHNPStuQUgCuBIdPv9dVkWXpLHd1wIDAQAB"; ----- DKIM dkm for

We configure the domain in OpenDKIM:

vi /etc/opendkim/opendkim.conf

Syslog                  yes
SyslogSuccess           yes
Canonicalization    relaxed/relaxed
Selector        smtp
KeyFile         /etc/opendkim/smtp.private
Socket                  inet:8891@localhost
SendReports             yes
PidFile /var/run/opendkim/
UserID milter
Statistics /var/lib/opendkim/stats.dat

We configure PostFix to use a milter (filter) in this case OpenDKIM:

vi /etc/postfix/

mydomain =

# Milter Configuration:
milter_default_action = accept
milter_protocol = 6

smtpd_milters = unix:/var/run/dk-filter/dk-filter.sock, inet:
non_smtpd_milters =

NOTE: The milter version depends on the postfix version, we can know the version with:

postconf -d mail_version

mail_version = 2.10.2
# Postfix ≥ 2.6
milter_protocol = 6
# 2.3 ≤ Postfix ≤ 2.5
milter_protocol = 2

NOTE: The maximum size of all DNS entries cannot exceed 512 bytes. In case of using several email signing systems, we should configure the same key for both systems ;)

We start the OpenDKIM service and add it to the default runlevel:

/etc/init.d/opendkim restart
/etc/init.d/postfix restart

rc-update add opendkim default
rc-update add postfix default

Now we will publish a TXT entry in the DNS zone of the domain in question with the content of:

cd /etc/opendkim/
cat smtp.txt

We can check that it has been published correctly by:

dig @ TXT

We disable Postfix debug:

vi /etc/postfix/

smtp      inet  n       -       n       -       -       smtpd
/etc/init.d/postfix restart

Another factor to consider is that the IP and name of our mail server match the direct and reverse:

dig 59 IN A A.B.C.D
host A.B.C.D
D.C.B.A....... domain name pointer

This is achieved by configuring the reverse as follows in case the resolution is delegated to one of our servers:

vi /etc/bind/


If the resolution is done by an ISP, we will have to configure it through the web interface provided by them. The truth is that it is rare for them to delegate the reverse resolution unless we have a good relationship with our ISP.

Finally, we will configure the DNS-SPF records so that when Gmail, Hotmail, etc. receive emails from our domain, they can verify that it was actually sent from one of our servers.

We configure a TXT entry in the domain zone with the following content: 86400 IN TXT "v=spf1 mx ip4:A.B.C.D -all 86400 IN TXT "spf2.0/mfrom,pra ip4:A.B.C.D -all"
dig @ TXT

In the previous entry, we are indicating that our server has the IP A.B.C.D and we do not want to accept mail from any other IP (-all). It should be noted that SPF records allow us to include other TXT entries, so we can mitigate the limitation of the 512 bytes per TXT record of the DNS.

With this, we will be able to enter the inbox of most mail systems such as Gmail, Hotmail, and others, as long as we don’t spam too much ;)

If you liked the article, you can treat me to a RedBull here