Home Server Dovecot IMAP Server

Index.

24-Jun-2011: initial release/
06-Jul-2011: fixes.
22-Aug-2011: updates MySQL tables.
31-Aug-2011: fixed missing Postfix SASL socket.
01-Sep-2011: Only TLS logins allowed.
27-Feb-2012: Namespaces have names.
14-Jun-2012: Fixed quota-warning emails.
07-Jan-2013: adjusted for Slackware
26-Aug-2013: configuration correction.
28-Nov-2013: changed imap_idle_notify_interval
14-Apr-2016: updated setting for stronger ssl security.

 

Introduction.

Dovecot is an open source IMAP and POP3 email server for Linux/UNIX-like systems, written with security primarily in mind. Dovecot is an excellent choice for both small and large installations. It’s fast, simple to set up, requires no special administration and it uses very little memory. We install it together with the Sieve plugin using the dovecot-pigeonhole package. The Dovecot Sieve plugin provides mail filtering facilities at time of final message delivery using the Sieve (RFC 5228) language. By writing Sieve scripts, users can customize how messages are delivered, e.g. whether they are forwarded or stored in special folders. On the desktop Thunderbird supports sieve using an extension.

On our Home Server Dovecot is used to deliver mail for users with the dovecot-lda program. Using this program has the benefit of using the Sieve plug in, and we use the same software (and possible bugs) to access the Maildir folders, so only programs from the same source manipulate the mail boxes.

I choose not to use POP3 for users, only IMAP. All the users mail stays on the server. This has the advantage that users can get their mail using different clients and different computers and still see their created sub mailboxes. If you should allow POP3, users will use that and all benefits of central mail storage is lost.

Dovecot has implemented shared namespaces. In this configuration each user has it’s own private mailbox of course, but they can share a mailbox too. You can use this for example by creating a family account, something like the mail account family@example.com, and then share that mailbox to every family member. I use such accounts for commercial sites that need a valid email address, and after you done with them they keep on sending Spam that account. These extra mail boxes are easy to ignore until you need it. Small companies using this concept of shared mailboxes can use this for things like sales@company.com and allow all sales people access to that mailbox.

You can use public mailboxes too, the shown configuration displays a helpdesk public mailbox.

 

Database setup.

First, using your phpMyAdmin installed using the MySQL article, add the database user mails with password mails (use your own much better password!) and create the database mails where this user has all privileges for. You can simply do that with phpMyAdmin using the privileges tab, add a new user, and in the new user screen select “Create database with the same name and grant all privileges”. Allow only access from localhost. We need this database for some extra features. Create three tables:

root@homsrv:~# echo "CREATE TABLE expires ( username varchar(100) not null,
               mailbox varchar(255) not null,
               expire_stamp integer not null,
               key (username, mailbox(200)) );" | mysql -u mails -pmails mails
root@homsrv:~# echo "CREATE TABLE user_shares ( from_user varchar(100) NOT NULL,
               to_user varchar(100) NOT NULL,
               dummy char(1) DEFAULT '1',
               PRIMARY KEY (from_user,to_user) );" | mysql -u mails -pmails mails
root@homsrv:~# echo "CREATE TABLE anyone_shares ( from_user varchar(100) NOT NULL,
               dummy char(1) DEFAULT '1',
               PRIMARY KEY (from_user) ); | mysql -u mails -pmails mails
root@homsrv:~#

Installation.

Build and install the dovecot and dovecot-sieve packages using Slackbuild scripts.

 

Configuration.

For all configuration files only the diff output is shown. Let’s start in directory /etc/dovecot with dovecot.conf:

--- dovecot.conf.orig   2011-06-24 22:17:18.000000000 +0200
+++ dovecot.conf        2011-06-24 22:19:47.000000000 +0200
@@ -17,13 +17,13 @@
 # --sysconfdir=/etc --localstatedir=/var

 # Protocols we want to be serving.
-#protocols = imap pop3 lmtp
+protocols = imap

 # A comma separated list of IPs or hosts where to listen in for connections.
 # "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
 # If you want to specify non-default ports or anything more complex,
 # edit conf.d/master.conf.
-#listen = *, ::
+listen = "*,[::]"

 # Base directory where to store runtime data.
 #base_dir = /var/run/dovecot/
@@ -74,7 +74,8 @@

 dict {
 #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
-  #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext
+  expire = mysql:/etc/dovecot/dovecot-dict-expire.conf
+  acl = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
 }

 # Most of the actual configuration gets included below. The filenames are

 

Create /etc/dovecot/dovecot-dict-expire.conf:

# dovecot-dict-expire.conf

connect = host=localhost dbname=mails user=mails password=xxxxx

map {
    pattern = shared/expire/$user/$mailbox
    table = expires
    value_field = expire_stamp

    fields {
        username = $user
        mailbox = $mailbox
    }
}

 

Create /etc/dovecot/dovecot-dict-sql.conf.ext:

connect = host=localhost dbname=mails user=mails password=mails

map {
    pattern = shared/shared-boxes/user/$to/$from
    table = user_shares
    value_field = dummy

    fields {
        from_user = $from
        to_user = $to
    }
}

map {
    pattern = shared/shared-boxes/anyone/$from
    table = anyone_shares
    value_field = dummy

    fields {
        from_user = $from
    }
}

 

Then in /etc/dovecot/conf.d are numbered configuration files that are included by the main configuration file. First copy 20-managesieve.conf and 90-sieve.conf from /usr/doc/dovecot-pigeonhole-n.n.n/example-config/conf.d to /etc/dovecot.conf.d. Then we start with 10-auth.conf and change it to use standard Unix accounts:

--- 10-auth.conf.orig   2011-06-24 22:24:58.000000000 +0200
+++ 10-auth.conf        2011-06-24 22:34:53.000000000 +0200
@@ -6,7 +6,7 @@
 # SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP
 # matches the local IP (ie. you're connecting from the same computer), the
 # connection is considered secure and plaintext authentication is allowed.
-#disable_plaintext_auth = yes
+disable_plaintext_auth = yes

 # Authentication cache size (e.g. 10M). 0 means it's disabled. Note that
 # bsdauth, PAM and vpopmail require cache_key to be set for caching to be used.
@@ -101,6 +101,15 @@
 ##
 ## Password and user databases
 ##
+passdb {
+    driver = shadow
+    args = blocking=no
+}
+userdb {
+    driver = passwd
+}

 #
 # Password database is used to verify user's password (and nothing more).
@@ -119,7 +126,7 @@
 #!include auth-deny.conf.ext
 #!include auth-master.conf.ext
 
-!include auth-system.conf.ext
+#!include auth-system.conf.ext
 #!include auth-sql.conf.ext
 #!include auth-ldap.conf.ext
 #!include auth-passwdfile.conf.ext

 

In 10-logging.conf set some extra logging:

--- 10-logging.conf.orig        2011-06-24 22:35:41.000000000 +0200
+++ 10-logging.conf     2011-06-24 22:36:36.000000000 +0200
@@ -47,6 +47,7 @@
 plugin {
 # Events to log. Also available: flag_change append
 #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
+  mail_log_events = expunge mailbox_delete mailbox_rename
 # Available fields: uid, box, msgid, from, subject, size, vsize, flags
 # size and vsize are available only for expunge and copy events.
 #mail_log_fields = uid box msgid size

 

In 10-mail.conf we set the Maildir as destination to deliver user mail, we enable standard private mail boxes, shared mail boxes and public mail boxes. As mail plugins expire, quota and acl are added.

--- /usr/doc/dovecot-2.2.23/dovecot/example-config/conf.d/10-mail.conf  2016-04-13 13:59:29.000000000 +0200
+++ 10-mail.conf        2016-04-13 21:44:53.000000000 +0200
@@ -27,7 +27,7 @@
 #
 # <doc/wiki/MailLocation.txt>
 #
-#mail_location = 
+mail_location = maildir:~/Maildir
 
 # If you need to set multiple mailbox locations or want to change default
 # namespace settings, you can do it by defining namespace sections.
@@ -41,12 +41,12 @@
 # on filesystem level to do so.
 namespace inbox {
   # Namespace type: private, shared or public
-  #type = private
+  type = private
 
   # Hierarchy separator to use. You should use the same separator for all
   # namespaces or some clients get confused. '/' is usually a good one.
   # The default however depends on the underlying mail storage format.
-  #separator = 
+  separator = /
 
   # Prefix required to access this namespace. This needs to be different for
   # all namespaces. For example "Public/".
@@ -80,25 +80,34 @@
 }
 
 # Example shared namespace configuration
-#namespace {
-  #type = shared
-  #separator = /
+namespace {
+  type = shared
+  separator = /
 
   # Mailboxes are visible under "shared/user@domain/"
   # %%n, %%d and %%u are expanded to the destination user.
-  #prefix = shared/%%u/
+  prefix = shared/%%u/
 
   # Mail location for other users' mailboxes. Note that %variables and ~/
   # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the
   # destination user's data.
-  #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u
+  location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u
 
   # Use the default namespace for saving subscriptions.
-  #subscriptions = no
+  subscriptions = no
 
   # List the shared/ namespace only if there are visible shared mailboxes.
   #list = children
-#}
+}
+
+# Public namespace
+namespace {
+  type = public
+  separator = /
+  prefix = Public/
+  location = maildir:/var/mail/public:INDEX=~/Maildir/public
+  subscriptions = no
+}
 # Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"?
 #mail_shared_explicit_inbox = no
 
@@ -212,7 +221,7 @@
 
 # Space separated list of plugins to load for all services. Plugins specific to
 # IMAP, LDA, etc. are added to this list in their own .conf files.
-#mail_plugins = 
+mail_plugins = $mail_plugins expire quota acl
 
 ##
 ## Mailbox handling optimizations

 

In 10-master.conf some sockets are set and given the correct permissions:

--- 10-master.conf.orig 2011-06-24 22:49:58.000000000 +0200
+++ 10-master.conf      2011-06-24 22:52:03.000000000 +0200
@@ -79,15 +79,19 @@
 # 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 auth-userdb {
-    #mode = 0600
+    mode = 0660
 #user =
-    #group =
+    group = users
 }

 # Postfix smtp-auth
-#unix_listener /var/spool/postfix/private/auth {
-#  mode = 0666
-#}
+unix_listener /var/spool/postfix-in/private/auth {
+    mode = 0600
+    user = postfix
+    group = postfix
+}
+  unix_listener auth-master {
+    mode = 0600
+  }
+  user = root

 # Auth process is run as this user.
 #user = $default_internal_user
@@ -104,8 +108,8 @@
 # If dict proxy is used, mail processes should have access to its socket.
 # For example: mode=0660, group=vmail and global mail_access_groups=vmail
 unix_listener dict {
-    #mode = 0600
+    mode = 0660
 #user =
-    #group =
+    group = users
 }
 }

 

In 10-ssl.conf point to our generated server certificates for mail.wpl.uk:

--- /usr/doc/dovecot-2.2.23/dovecot/example-config/conf.d/10-ssl.conf   2016-04-13 13:59:29.000000000 +0200
+++ 10-ssl.conf 2016-04-13 21:50:45.000000000 +0200
@@ -9,8 +9,8 @@
 # dropping root privileges, so keep the key file unreadable by anyone but
 # root. Included doc/mkcert.sh can be used to easily generate self-signed
 # certificate, just make sure to update the domains in dovecot-openssl.cnf
-ssl_cert = </etc/ssl/certs/dovecot.pem
-ssl_key = </etc/ssl/private/dovecot.pem
+ssl_cert = </etc/certs/wpl-mail.crt
+ssl_key = </etc/certs/wpl-mail.pem
 
 # If key file is password protected, give the password here. Alternatively
 # give it when starting dovecot with -p parameter. Since this file is often
@@ -43,16 +43,16 @@
 #ssl_cert_username_field = commonName
 
 # DH parameters length to use.
-#ssl_dh_parameters_length = 1024
+ssl_dh_parameters_length = 2048
 
 # SSL protocols to use
-#ssl_protocols = !SSLv2
+ssl_protocols = !SSLv2 !SSLv3
 
 # SSL ciphers to use
-#ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL
+ssl_cipher_list = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
 
 # Prefer the server's order of ciphers over client's.
-#ssl_prefer_server_ciphers = no
+ssl_prefer_server_ciphers = yes
 
 # SSL crypto device to use, for valid values run "openssl engine"
 #ssl_crypto_device =

 

In 15-lda.conf add the postmaster’s email address for cases that the lda needs to send bounce messages. Add the sieve plugin here too:

--- 15-lda.conf.orig    2011-06-24 22:55:28.000000000 +0200
+++ 15-lda.conf 2011-06-24 22:56:18.000000000 +0200
@@ -4,11 +4,11 @@

 # Address to use when sending rejection mails.
 # Default is postmaster@<your domain>.
-#postmaster_address =
+postmaster_address = postmaster@wpl.uk

 # Hostname to use in various parts of sent mails, eg. in Message-Id.
 # Default is the system's real hostname.
-#hostname =
+hostname = mail.wpl.uk

 # If user is over quota, return with temporary failure instead of
 # bouncing the mail.
@@ -44,5 +44,5 @@

 protocol lda {
 # Space separated list of plugins to load (default is global mail_plugins).
-  #mail_plugins = $mail_plugins
+  mail_plugins = $mail_plugins sieve
 }

 

The file 15-mailboxes.conf should be used without changes.
In 20-imap.conf add acl and quota plugins:

--- /usr/doc/dovecot-2.2.23/dovecot/example-config/conf.d/20-imap.conf  2016-04-13 13:59:29.000000000 +0200
+++ 20-imap.conf        2016-04-13 22:07:43.000000000 +0200
@@ -67,7 +67,7 @@
 
 protocol imap {
   # Space separated list of plugins to load (default is global mail_plugins).
-  #mail_plugins = $mail_plugins
+  mail_plugins = $mail_plugins imap_acl imap_quota
 
   # Maximum number of IMAP connections allowed for a user from each IP address.
   # NOTE: The username is compared case-sensitively.

 

In 20-managesive.conf add sieve to the protocols, add listener port and the deprecated listener port:

--- 20-managesieve.conf.orig    2011-06-24 23:04:22.000000000 +0200
+++ 20-managesieve.conf 2011-06-24 23:05:16.000000000 +0200
@@ -3,18 +3,18 @@
 ##

 # Uncomment to enable managesieve protocol:
-#protocols = $protocols sieve
+protocols = $protocols sieve

 # Service definitions

 service managesieve-login {
-  #inet_listener sieve {
-  #  port = 4190
-  #}
+  inet_listener sieve {
+    port = 4190
+  }

-  #inet_listener sieve_deprecated {
-  #  port = 2000
-  #}
+  inet_listener sieve_deprecated {
+    port = 2000
+  }

 # Number of connections to handle before starting a new process. Typically
 # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0

 

In 90-acl.conf set the method to get to the shared mailbox information:

--- 90-acl.conf.orig    2011-06-24 22:58:09.000000000 +0200
+++ 90-acl.conf 2011-06-24 22:58:59.000000000 +0200
@@ -15,5 +15,10 @@
 # To let users LIST mailboxes shared by other users, Dovecot needs a
 # shared mailbox dictionary. For example:
 plugin {
-  #acl_shared_dict = file:/var/lib/dovecot/shared-mailboxes
+  acl_shared_dict = proxy::acl
+}
+
+plugin {
+  # Without global ACLs:
+  acl = vfile
 }

 

In 90-quota.conf set the mailbox quota’s:

--- /usr/doc/dovecot-2.2.23/dovecot/example-config/conf.d/90-quota.conf 2016-04-13 13:59:29.000000000 +0200
+++ 90-quota.conf       2016-04-13 22:10:04.000000000 +0200
@@ -17,6 +17,8 @@
 plugin {
   #quota_rule = *:storage=1G
   #quota_rule2 = Trash:storage=+100M
+  quota_rule = *:storage=400MB
+  quota_rule2 = Trash:storage=+20%%
 
   # LDA/LMTP allows saving the last mail to bring user from under quota to
   # over quota, if the quota doesn't grow too high. Default is to allow as
@@ -38,6 +40,8 @@
 plugin {
   #quota_warning = storage=95%% quota-warning 95 %u
   #quota_warning2 = storage=80%% quota-warning 80 %u
+  quota_warning = storage=95%% /usr/local/sbin/quota-warning.sh 95
+  quota_warning2 = storage=80%% /usr/local/sbin/quota-warning.sh 80
 }
 
 # Example quota-warning service. The unix listener's permissions should be
@@ -65,7 +69,8 @@
 
 plugin {
   #quota = dirsize:User quota
-  #quota = maildir:User quota
+  quota = maildir:User quota
+  quota2 = maildir:Shared quota:ns=Public/
   #quota = dict:User quota::proxy::quota
   #quota = fs:User quota
 }

 

And finally in 90-sieve.conf change the scripts directory to a hidden directory:

--- 90-sieve.conf.orig  2011-06-24 23:05:56.000000000 +0200
+++ 90-sieve.conf       2011-06-24 23:06:21.000000000 +0200
@@ -16,7 +16,7 @@
 #sieve_global_path = /var/lib/dovecot/sieve/default.sieve

 # Directory for :personal include scripts for the include extension.
-  sieve_dir = ~/sieve
+  sieve_dir = ~/.sieve

 # Directory for :global include scripts for the include extension.
 #sieve_global_dir =

 

Setup shared mailboxes.

Next we must create the structure for a shared public mailbox, the example is a helpdesk mailbox:

root@homsrv:~# cd /var/mail
[root@homsrv mail]# mkdir public
[root@homsrv mail]# chgrp users public
[root@homsrv mail]# chmod 770 public
[root@homsrv mail]# chmod g+s public
[root@homsrv mail]# cd public
[root@homsrv public]# touch dovecot-shared
[root@homsrv public]# chmod 660 dovecot-shared
[root@homsrv public]# mkdir .helpdesk
[root@homsrv public]# chmod 770 .helpdesk
[root@homsrv public]# chmod g+s .helpdesk
[root@homsrv public]# touch .helpdesk/dovecot-shared
[root@homsrv public]# chmod 660 .helpdesk/dovecot-shared
[root@homsrv public]# mkdir .helpdesk/{cur,new,tmp}
[root@homsrv public]# chmod 770 .helpdesk/{cur,new,tmp}
[root@homsrv public]# echo "authenticated lrwstipe" > .helpdesk/dovecot-acl
[root@homsrv public]# ls -la
total 12
drwxrws--- 3 root users 4096 Jun 25 19:03 .
drwxrwxrwt 3 root root  4096 Jun 25 19:02 ..
-rw-rw---- 1 root users    0 Jun 25 19:03 dovecot-shared
drwxrws--- 5 root users 4096 Jun 25 19:05 .helpdesk
[root@homsrv public]# ls -la .helpdesk
total 24
drwxrws--- 5 root users 4096 Jun 25 19:05 .
drwxrws--- 3 root users 4096 Jun 25 19:03 ..
drwxrws--- 2 root users 4096 Jun 25 19:04 cur
-rw-r--r-- 1 root users   23 Jun 25 19:05 dovecot-acl
-rw-rw---- 1 root users    0 Jun 25 19:03 dovecot-shared
drwxrws--- 2 root users 4096 Jun 25 19:04 new
drwxrws--- 2 root users 4096 Jun 25 19:04 tmp
[root@homsrv public]#

 

Final setup steps.

Create /etc/cron.daily/dovecot and chmod it 755:

#!/bin/sh
#
# Dovecot expire messages

/usr/bin/doveadm expunge -A mailbox Trash savedbefore 7d 2> /dev/null
/usr/bin/doveadm expunge -A mailbox Spam savedbefore 7d 2> /dev/null

 

This will daily delete messages from the users Trash and Spam mailboxes that have been put there after 7 days. Then in /etc/postfix-in/main.cf change the line

home_mailbox = Maildir/

into:

mailbox_command = /usr/lib/dovecot/dovecot-lda -d "$USER"

 

Start dovecot using /etc/rc.d/init.d/dovecot start. Restart postfix using /etc/rc.d/rc.sendmail restart. Now the mail will be delivered via the dovecot LDA. To start Dovecot at boot (works with the Slackbuild from here), run pkgtool -> setup -> dovecot to enable it at boot.

 

Sample sieve script.

Here is a sample sieve filter script:

#
# mailinglists
#
require ["fileinto", "envelope"];

if header :contains "X-DSPAM-Result" "Spam" {
 fileinto "Spam";
}

elsif anyof (address :is "to" "ardour-dev",
 header :contains "X-Original-To" "ardour-dev",
 header :contains "List-Id" "ardour-dev-ardour.org") {
 fileinto "lists.ardour-dev";
}

elsif anyof (address :is "to" "ardour-users",
 header :contains "X-Original-To" "ardour-users",
 header :contains "List-Id" "ardour-users-ardour.org") {
 fileinto "lists.ardour-users";
}

elsif anyof (address :is "to" "jamin-dev",
 header :contains "X-Original-To" "jamin-dev",
 header :contains "List-Id" "jamin-devel.lists.sourceforge.net") {
 fileinto "lists.jamin-dev";
}

elsif anyof (address :is "to" "linux-audio-announce@lists.linuxaudio.org",
 header :contains "List-Id" "A list for announcements about Linux Audio",
 header :contains "X-Original-To" "linux-audio-announce") {
 fileinto "lists.linux-audio-announce";
}

 

Postfix SASL.

In the Postfix mail server configuration of the postfix-in instance, the SASL configuration lines were commented out. If you want to use SASL for postfix, uncomment these lines in /etc/postfix-in/main.cf   and restart postfix.

 

Download.

See the download page for the script and configuration files.