Mail Server

From Funtoo
Jump to navigation Jump to search

How to set up a simple, secure, lightweight email server using Postfix and Dovecot

Running one's own email server doesn't have to be mystical and impenetrable; using a simple MTA like Postfix along with an LDA like Dovecot makes the task relatively easy. Regrettably, good information on how to do this is hard to come by. What this guide will help you do is install a mail server which uses a database backend to manage domains and users, and features mail delivery via POP3 and/or IMAP.


If you intend to run your own email server, you will need to have DNS with at least one MX record on a DNS server that can be seen by the Internet at large. It is also essential for reliable mail delivery to have properly-configured reverse DNS as many mail servers will use reverse DNS and will expect your IP address to resolve to your advertised hostname. Setting up such a thing is beyond the scope of this document.


The following packages need to be installed first, before we can do anything: mail-mta/postfix, net-mail/dovecot, and dev-db/mariadb. Before we emerge these, however, we must ensure some USE flags are properly set first:

   /etc/portage/package.use/mail-server - USE flags
mail-mta/postfix dovecot-sasl mysql pam ssl
net-mail/dovecot bzip2 maildir mysql pam ssl zlib

With USE flags properly set, we can emerge our packages:

root # emerge -avq postfix mariadb

Setting the dovecot-sasl USE flag should pull in net-mail/dovecot. If it does not, emerge this way:

root # emerge -avq postfix dovecot mariadb

Next, we need to set up the location on the server where email will be delivered:

root # mkdir /mailstore
root # chgrp mail /mailstore
root # chmod -R g+rw /mailstore


Now we come to the meat of the project. First we will have to set up the mail user/domain database, then we will have to configure Postfix, then finally, configure Dovecot. At the end of this procedure, we should have a fully functioning mail server.

Setting up the Database

First step is to set up the database for the virtual domain/user tracking. We need to set up the database's root user and get the database up and running (be sure to replace <strong-password> with a real, strong password):

root # mysqladmin -u root password '<strong-password>'
root # rc-update add mysql default
root # rc

Next, we need to login to MySQL (you will have to enter the <strong-password> you set above):

root # mysql -p

Now, we create the database and its tables (again, replace <mailuserpass> with a real password):

mysql> CREATE DATABASE mailserver;
mysql> USE mailserver;
mysql> GRANT SELECT ON mailserver.* TO 'mailuser'@'' IDENTIFIED BY '<mailuserpass>';
mysql> CREATE TABLE virtual_domains (id INT(11) NOT NULL AUTO_INCREMENT,
root ##i##       name VARCHAR(50) NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
mysql> CREATE TABLE virtual_users (id INT(11) NOT NULL AUTO_INCREMENT,
root ##i##       domain_id INT(11) NOT NULL, password VARCHAR(106) NOT NULL, email VARCHAR(100) NOT NULL,
root ##i##       PRIMARY KEY (id), UNIQUE KEY email (email), FOREIGN KEY (domain_id) REFERENCES virtual_domains(id)
mysql> CREATE TABLE virtual_aliases (id INT(11) NOT NULL AUTO_INCREMENT,
root ##i##       domain_id INT(11) NOT NULL, source VARCHAR(100) NOT NULL, destination VARCHAR(100) NOT NULL,
root ##i##       PRIMARY KEY (id), FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE)
root ##i##       ENGINE=InnoDB DEFAULT CHARSET=utf8;

Now that we've created our database and tables, we need to put our domain into it. Replace <> with the FQDN of that will go to the right of the '@' sign in email addresses on your mail domain:

mysql> INSERT INTO virtual_domains VALUES (DEFAULT, '<>');

If you're planning on receiving mail for more than one domain, you can add them by reusing the previous query and changing <> to the other domain(s); you will have to enter one query for each extra domain.

Next, we need to populate that database with users (the part that goes on the left side of the '@' sign). Again, these need to be added one at a time. For each entry in the database, we will need a username and a password; since we want these passwords to be strong, we will use doveadm to generate them:

root # doveadm pw -s SHA512-CRYPT
Enter new password: 
Retype new password: 

You will be prompted to enter the password twice before it gives back the hash. The part that comes after {SHA512-CRYPT} is the password that will need to go into the database (it will always start with $6$).


The password you will distribute to your users is the one you typed into doveadm; the hash that it outputs is what will go into the virtual_users table.

Replace <pw_hash> with the output of doveadm (starting with $6$), and <> with the email address for the user you're creating:

mysql> INSERT INTO virtual_users VALUES (DEFAULT, 1, '<pw_hash>', '<>');

The second field in the query above (the '1') is the ID of the entry in the virtual_domains table. If you're only using one domain, you don't have to worry about changing it; otherwise, you will have to change it to correspond to the domain for that user. You can find out what IDs they have with the following query:

mysql> SELECT * FROM virtual_domains;

Once you are done entering users you can leave MySQL:

mysql> quit

Configuring Postfix

Now we have to configure Postfix. Pull up your favorite text editor and add the following lines to the bottom of /etc/postfix/

   /etc/postfix/ - Postfix configuration
# SASL config
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination

# TLS config
smtpd_tls_cert_file = /etc/ssl/certs/dovecot.pem
smtpd_tls_key_file = /etc/ssl/private/dovecot.pem
smtpd_use_tls = yes
smtpd_tls_auth_only = yes
smtp_tls_security_level = may
smtp_tls_loglevel = 2
smtpd_tls_received_header = yes

# Authentication config
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/
virtual_mailbox_maps = mysql:/etc/postfix/
virtual_alias_maps = mysql:/etc/postfix/
local_recipient_maps = $virtual_mailbox_maps

Next, we have to change a few items in the same config file (we will be changing the defaults in the file to what's shown here). Since this is a new install, the developers recommended that the compatibility_level be set to 2:

   /etc/postfix/ - More Postfix configuration
compatibility_level = 2

Next, we will be setting up the mail server's hostname and domain. How we fill this in depends on what your DNS and MX records point to. If you have it set up so that your main domain is of the form tld.ext, then you will put that into the mydomain field, otherwise, you will set it the same as the myshostname field (in host.tld.ext form):

   /etc/postfix/ - More Postfix configuration
myhostname = <>
mydomain = < |>

The mydestination field MUST be set to localhost, otherwise, incoming mail will bounce:

   /etc/postfix/ - More Postfix configuration
mydestination = localhost # This MUST be set to localhost

Some mail servers will not talk to you if the hostname that is set up on your reverse DNS record does not match the SMTP banner that Postfix sends to peers. To fix that, add the following (replace <reverse DNS hostname> with your real reverse DNS hostname):

   /etc/postfix/ - And yet more Postfix configuration
smtpd_banner = <reverse DNS hostname> ESMTP $mail_name

It is not necessary for the reverse DNS hostname to match your mail server's hostname; it just has to be present.

Finally, in this file, we have to enumerate the networks that can relay mail via our server. Generally we want to list only the subnets that we want to be able to send mail from (replace <LAN IP> with your LAN's subnet and <LAN netmask> with your LAN's netmask, and leave in):

   /etc/postfix/ - More Postfix configuration
mynetworks = <LAN IP>/<LAN netmask>,

If you want one or more remote hosts to be able to send through your mail server, you should add them to the mynetworks line as comma separated values. Also, you should set the netmask (the part after the '/') on each of them to 32, to ensure that only those IP addresses can be sent from.

Next, we have to create the files referenced above as part of the 'Authentication config'. First, we have to create /etc/postfix/ (be sure to replace <mailuserpass> with mailuser's real password):

   /etc/postfix/ - MySQL/virtual domains Postfix configuration
user = mailuser
password = <mailuserpass>
hosts =
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s'

Next, we have to create /etc/postfix/

   /etc/postfix/ - MySQL/virtual maps Postfix configuration
user = mailuser
password = <mailuserpass>
hosts =
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s'

And finally, we have to create /etc/postfix/

   /etc/postfix/ - MySQL/virtual alias maps Postfix configuration
user = mailuser
password = <mailuserpass>
hosts =
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source='%s'

If we want Postfix to talk on port 25, we have to make sure that the second field in the line in /etc/postfix/ for smtp is inet:

   /etc/postfix/ - Postfix master service file
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (no)    (never) (100)
# ==========================================================================
smtp      inet  n       -       y       -       -       smtpd

Some ISPs block port 25; in that case you would uncomment the line in that starts with "submission" (remove the '#' character in front of it) to enable postfix communicating on port 587.

Now lets start Postfix and make sure that our authentication queries are working:

root # /etc/init.d/postfix start
root # postmap -q <> mysql:/etc/postfix/
root # postmap -q <user>@<> mysql:/etc/postfix/

Assuming both postmap commands returned 1, we can go on to configuring Dovecot.

Configuring Dovecot

Now that Postfix is properly configured, it's time to tackle Dovecot. The first file we want to look at is /etc/dovecot/dovecot.conf. In particular, we want to make sure the protocols line has imap, pop3, and lmtp enabled:

   /etc/dovecot/dovecot.conf - Dovecot configuration
protocols = imap pop3 lmtp

Next we need to look at /etc/dovecot/conf.d/10-mail.conf. We need to tell Dovecot where to store mail (and, in the case of IMAP, keep it). mail_location and mail_privileged_group will likely be in there already and need to be changed; we will likely have to add first_valid_uid:

   /etc/dovecot/conf.d/10-mail.conf - Dovecot configuration
mail_location = maildir:/mailstore/%d/%n
mail_privileged_group = mail
first_valid_uid = 0

Next is /etc/dovecot/conf.d/10-auth.conf: Here we have to tell Dovecot how we want to authenticate our users. Note that in addition to setting disable_plaintext_auth to yes and auth_mechanisms to plain login, we need to comment out (by inserting a '#' in front of) the line !include auth-system.conf.ext and uncomment (by removing any '#' in front of) the line !include auth-sql.conf.ext. This is to prevent Dovecot from using native accounts for authorization and use our database instead:

   /etc/dovecot/conf.d/10-auth.conf - Dovecot authorization config
disable_plaintext_auth = yes
auth_mechanisms = plain login
#!include auth-system.conf.ext
!include auth-sql.conf.ext

Next we need to edit /etc/dovecot/conf.d/auth-sql.conf.ext, so Dovecot knows where and how the passwords are stored, and how and where to write our users' mail:

   /etc/dovecot/conf.d/auth-sql.conf.ext - Dovecot SQL config
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
userdb {
  driver = static
  args = uid=mail gid=mail home=/mailstore/%d/%n

Next is /etc/dovecot/dovecot-sql.conf.ext, which is mentioned in the previous file. This is to tell Dovecot the details of how to talk to the database in order to validate user logins (replace <mailuserpass> with the password you created for the MySQL user 'mailuser'):

   /etc/dovecot/dovecot-sql.conf.ext - More Dovecot SQL config
driver = mysql
connect = host= dbname=mailserver user=mailuser password=<mailuserpass>
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

Next file we have to modify is /etc/dovecot/conf.d/10-master.conf. First, we will set the listener ports for IMAP and POP3 to zero, to force encrypted links:

   /etc/dovecot/conf.d/10-master.conf - Dovecot master config file
service imap-login {
  inet_listener imap {
    port = 0

service pop3-login {
  inet_listener pop3 {
    port = 0

Next, we have to configure Dovecot's LMTP service:

   /etc/dovecot/conf.d/10-master.conf - Dovecot master config file
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0666
    group = postfix
    user = postfix
  # Create inet listener only if you can't use the above UNIX socket
  #inet_listener lmtp {
    # Avoid making LMTP visible for the entire internet
    #address =
    #port =

Finally, we need to properly set up the auth and auth-worker services:

   /etc/dovecot/conf.d/10-master.conf - Dovecot master config file
service auth {
  # auth_socket_path points to this userdb socket by default. It's typically
  # used by dovecot-lda, doveadm, possibly imap process, etc. Its default
  # permissions make it readable only by root, but you may need to relax these
  # permissions. Users that have access to this socket are able to get a list
  # of all usernames and get results of everyone's userdb lookups.
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  unix_listener auth-userdb {
    mode = 0600
    user = mail
    #group =
  # Postfix smtp-auth
  #unix_listener /var/spool/postfix/private/auth {
  #  mode = 0666
  # Auth process is run as this user.
  user = dovecot
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 = mail

And last, but not least, we need to edit /etc/dovecot/conf.d/10-ssl.conf, so that Dovecot knows where to find valid certificates to work with:

   /etc/dovecot/conf.d/10-ssl.conf - Dovecot SSL config
ssl_cert = </etc/ssl/certs/dovecot.pem
ssl_key = </etc/ssl/private/dovecot.pem
ssl = required

We now need to generate the SSL certificates that Postfix and Dovecot are looking for. When it asks for a FQDN for the certificate, make sure to put in the FQDN of the mail server:

root # openssl req -new -x509 -days 1000 -nodes -out "/etc/ssl/certs/dovecot.pem" -keyout "/etc/ssl/private/dovecot.pem"

Yes, the certificates generated this way are self-signed; if that bothers you feel free to buy one from GoDaddy or some other CA. It won't make things more secure (self-signed certificates have an undeserved bad reputation), but it will make you slightly poorer and the CA slightly richer.

Finally, we set the permissions on the Dovecot config files so they belong to mail:dovecot and nobody else:

root # chown -R mail:dovecot /etc/dovecot
root # chmod -R o-rwx /etc/dovecot

Final Steps

We want Postfix and Dovecot to come up when our server boots up, so we need to add them to the server's startup; once that's done, we'll start Dovecot with the rc command:

root # rc-update add postfix default
root # rc-update add dovecot default
root # rc

With that, the mail server should be configured correctly to send and receive email. If it doesn't work, you will probably want to snoop around /var/log/messages and look for lines that have postfix or dovecot in them for clues.

Client Configuration

This configuration is for Thunderbird, but it should be applicable to any other client. When setting up a new account, it will ask for your name, email address, and password. Clicking on the Continue button will then have Thunderbird attempt to autodetect your mail server settings automagically; this should normally fail (if not, then you're done!). If you look in /var/log/messages on the mail server, you should see something similar to this:

   /var/log/messages - System log file
postfix/smtpd[]: improper command pipelining after EHLO from <client FQDN>[<client IP>]: QUIT\r\n

The solution then is to select port 993 from the Port: combobox on the Incoming: line. Hitting the Re-test button should allow Thunderbird to properly detect the settings at this point, assuming that the following is true:

  • The server hostname fields contain the FQDN of your mail server
  • The Incoming: and Outgoing: username fields contain the user's full email address
  • The password given for the user's email address is correct.

If all else fails, you can try the following settings:

Incoming:IMAPmail server's FQDN993SSL/TLSNormal password
Outgoing:SMTPmail server's FQDN25STARTTLSNormal password

Once the settings are correct in Thunderbird, the first time you send or receive an email message, Thunderbird will ask you to confirm that you want to use the certificates coming from your email server if they are self-signed.

A Few Words on Security, Spam & Blacklists

The email server you have just set up should be reasonably secure from attackers; it won't relay messages outside of your LAN and it won't talk to unencrypted peers. As long as you and your users have chosen good, strong passwords for each link of the chain, you shouldn't have to worry too much about such as bad actors, or being put on spam blacklists. As long as you keep an eye on your mail server and investigate suspicious activity, it should serve you well and work well in the wider Internet environment.

But Wait, There's More!

But only a bit more. Those are the basics, but if you want you can also set up SPF, DKIM, PTR records; unfortunately those are beyond the scope of this article. Other possibilities are spam filtering, push support, and full text-search; these are left as an exercise for the reader.