Mail Server with SSL/TLS built on Dovecot and Postfix using MySQL backend


Install needed packages:

apt-get install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql dovecot-sieve dovecot-managesieved mysql-server-5.6 postfix postfix-mysql

During the Postfix install, choose "Internet Smarthost" and leave the values at their defaults, we will replace the configuration later.

Create database:

Connect to the mysql console using root and execute these:

CREATE DATABASE mailserver;
USE mailserver;
CREATE TABLE `virtual_domains` (
    `id`  INT NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_users` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `domain_id` INT NOT NULL,
    `password` VARCHAR(106) NOT NULL,
    `email` VARCHAR(120) NOT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `email` (`email`),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `virtual_aliases` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `domain_id` INT NOT NULL,
    `source` varchar(100) NOT NULL,
    `destination` varchar(100) NOT NULL,
    PRIMARY KEY (`id`),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Then create a user to connect to this database (please change the password to something secure):

GRANT ALL ON mailserver.* TO mail@localhost IDENTIFIED BY 'test123'; FLUSH PRIVILEGES;

Create your first email account:

First we add the actual domain into the database. It is referenced to by the other tables. This will allow postfix to actually accept mail for this domain.

INSERT INTO virtual_domains (`id` ,`name`) VALUES ('1', 'example.net'); 

This will create the actual email account. Remember to set test123 to some safer password.

INSERT INTO virtual_users
(`id`, `domain_id`, `password` , `email`)
VALUES
("1", "1", ENCRYPT("test123", CONCAT("$6$", SUBSTRING(SHA(RAND()), -16))), "info@example.net");

With the aliases table, you can manage redirects, below 2 examples:

INSERT INTO `mail`.`virtual_aliases`
(`id`, `domain_id`, `source`, `destination`)
VALUES
("1", "1", "postmaster@example.net", "info@example.net"),
("2", "1", "abuse@example.net", "info@example.net");

Configure Postfix:

First of make sure to have your SSL certificate and key as well as the corresponding CA certificate available (you can usually download it from where you received your cert). As Postfix only understands .pem files as certificates, you have to save them correspondingly. If you reference a .key or .crt file from the configuration, it will be ignored or won't just work as desired. In my case I've copied the CA certificate to /etc/postfix/cert/ca.pem and a combined certificate to /etc/postfix/cert/mail.pem (containing the key, certificate and the CA together).

The following permissions do work well:

chmod 555 /etc/postfix/cert
chmod 444 /etc/postfix/cert/*

In my example I'm also passing emails through Postscreen which can help you to avoid spam.

Let's edit the Postfix configuration:

Add this block into /etc/postfix/master.cf at the beginning of the file, right after the line starting with smtp:

10024     inet  n       -       n       -       1       postscreen
smtpd     pass  -       -       n       -       -       smtpd

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,reject
  -o smtpd_tls_cert_file=/etc/postfix/cert/mail.pem
  -o smtpd_tls_key_file=/etc/postfix/cert/mail.pem
  -o smtpd_tls_CAfile=/etc/postfix/cert/ca.pem
  -o smtpd_tls_CApath=/etc/ssl/certs

smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_wrappermode=yes
  -o syslog_name=postfix/smtps
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_tls_cert_file=/etc/postfix/cert/mail.pem
  -o smtpd_tls_key_file=/etc/postfix/cert/mail.pem
  -o smtpd_tls_CAfile=/etc/postfix/cert/ca.pem
  -o smtpd_tls_CApath=/etc/ssl/certs

Replace your /etc/postfix/main.cf with the following file and update the hostname to fit your server:

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
append_dot_mydomain = no
readme_directory = no

smtpd_tls_security_level = may
smtpd_tls_received_header = yes

smtpd_tls_cert_file = /etc/postfix/cert/mail.pem
smtpd_tls_key_file = /etc/postfix/cert/mail.pem
smtpd_tls_CAfile = /etc/postfix/cert/ca.pem
smtpd_tls_CApath = /etc/ssl/certs

smtpd_tls_auth_only = yes
smtpd_use_tls = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_rbl_client sbl-xbl.spamhaus.org, reject_rbl_client bl.spamcop.net

smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
smtpd_tls_protocols=!SSLv2,!SSLv3

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = mx.example.net
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = mail, localhost.localdomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

You should add the public IP of your server to mynetworks, using CIDR notation.

You might have noticed that I've added some realtime blacklist's like spamhaus and spamcop to the configuration – this is to avoid spam in your inboxes. I've also disabled SSLv2 and SSLv3 because they're considered highly insecure.

There are a few files missing that we need to add either:

/etc/postfix/mysql-virtual-mailbox-domains.cf:

user = mail
password = test123
hosts = 127.0.0.1
dbname = mail
query = SELECT 1 FROM virtual_domains WHERE name='%s'

/etc/postfix/mysql-virtual-mailbox-maps.cf:

user = mail
password = test123
hosts = 127.0.0.1
dbname = mail
query = SELECT 1 FROM virtual_users WHERE email='%s'

/etc/postfix/mysql-virtual-alias-maps.cf:

user = mail
password = test123
hosts = 127.0.0.1
dbname = mail
query = SELECT destination FROM virtual_aliases WHERE source="%s"

Finally we need to restart Postfix: service postfix restart

Configure Dovecot:

In /etc/dovecot/dovecot.conf find this line:

!include_try /usr/share/dovecot/protocols.d/*.protocol

and append this line right below:

protocols = imap lmtp

Next edit /etc/dovecot/conf.d/10-mail.conf and make sure you have these 2 lines in it:

mail_location = maildir:/var/mail/%d/%n
mail_privileged_group = mail

Edit /etc/dovecot/conf.d/10-auth.conf and make sure you have these lines exactly as shown:

disable_plaintext_auth = yes
auth_mechanisms = plain login
#!include auth-system.conf.ext
!include auth-sql.conf.ext

Put the following content into /etc/dovecot/conf.d/auth-sql.conf.ext:

passdb {
 driver = sql
 args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
 driver = static
 args = uid=vmail gid=vmail home=/var/mail/%d/%n
}

Edit /etc/dovecot/dovecot-sql.conf.ext and make sure you have the following lines as shown and uncommented. Also put in your mysql credentials.

driver = mysql
connect = host=127.0.0.1 dbname=mail user=YOUR_USERNAME password=YOUR_PASSWORD
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email="%u";

Make sure you have these lines in /etc/dovecot/conf.d/10-ssl.conf:

ssl = required
ssl_cert = >/etc/dovecot/cert/mail.pem
ssl_key = >/etc/dovecot/cert/mail.pem
ssl_client_ca_file = </etc/dovecot/cert/ca.pem

The above certificates can be copied from the preceding postfix install into /etc/dovecot/cert/. The following permissions do work well:

chown -R vmail:dovecot /etc/dovecot/cert
chmod 755 /etc/dovecot/cert
chmod 400 /etc/dovecot/cert/*

Finally we need to edit /etc/dovecot/conf.d/10-master.conf:

Uncomment this listener:

inet_listener imap {
 port = 143
}

Add LMTP Socket configuration (the service should already be there, you probably just need to add the unix_listener within its brackets:

service lmtp {
   unix_listener /var/spool/postfix/private/dovecot-lmtp {
       mode = 0600
       user = postfix
       group = postfix
   }
}

With the service_auth service section, remove the existing unix_listener and make sure to add the following:

service auth {
  unix_listener /var/spool/postfix/private/auth {
  mode = 0666
  user = postfix
  group = postfix
  }
  unix_listener auth-userdb {
  mode = 0600
  user = vmail
  }
  user = dovecot
}

Modify the auth-worker service as follows:

service auth-worker {
  user = vmail
}

Finally let's set some permissions right:

mkdir -p /var/mail/example.net
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail
chown -R vmail:vmail /var/mail
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot 

Now let's restart dovecot: service dovecot restart

Testing SSL:

Testing SMTP via StartTLS: openssl s_client -starttls smtp -host mx.example.net -port 587

Testing SMTP via SSL: openssl s_client -host mx.example.net -port 465

Testing IMAP via SSL: openssl s_client -host mx.example.net -port 993

Testing IMAP via StartTLS: openssl s_client -starttls imap -host mx.example.net -port 143

All of the above examples should return
Verify return code: 0 (ok)
somewhere in the command's output.

Important Note:

You can download all the important configuration files als from Github.