5. Courier-IMAP

Now that our server can send and receive email, it sounds reasonable to let users read it! For this purpose, we're going to install Courier-IMAP, a fast, scalable, enterprise IMAP server that uses Maildirs. This is the same IMAP server that comes with the Courier mail server, but configured as a standalone IMAP server that can be used with other mail servers, such as Postfix.

5.1 Installation and configuration

The following is the list of the required packages:

Once you have added all the packages, you will find a fresh new /etc/courier/ directory containing Courier IMAP's configuration files. Let's take a brief look at each of them.

The /etc/courier/authdaemonrc configuration file sets several operational parameters for the authdaemond process (the resident authentication daemon); fortunately, we only need to edit the authmodulelist parameter, which specifies the list of available authentication modules; set it to authmysql to exclusively enable MySQL based authentication:

/etc/courier/authdaemonrc
[ ... ]
authmodulelist="authmysql"
[ ... ]

The /etc/courier/authmysqlrc configuration file contains the authmysql database connection parameters; below is a sample configuration file:

/etc/courier/authmysqlrc
MYSQL_SERVER         127.0.0.1
MYSQL_USERNAME       vmail
MYSQL_PASSWORD       vmail
# If you connect through the socket:
#MYSQL_SOCKET         /path/to/mysql.sock
#MYSQL_PORT           0
MYSQL_PORT           3306
MYSQL_OPT            0
MYSQL_DATABASE       mail
MYSQL_USER_TABLE     users
MYSQL_CRYPT_PWFIELD  password
MYSQL_DEFAULT_DOMAIN kernel-panic.it
MYSQL_UID_FIELD      uid
MYSQL_GID_FIELD      gid
MYSQL_LOGIN_FIELD    login
MYSQL_HOME_FIELD     home
MYSQL_NAME_FIELD     name
MYSQL_MAILDIR_FIELD  maildir
MYSQL_QUOTA_FIELD    quota
# MYSQL_WHERE_CLAUSE	field=value AND field=value...

The next step is creating the SSL certificate for the IMAPS protocol. To make your life easier, Courier-IMAP comes with a script, mkimapdcert(8), which will create the certificate after reading all the necessary configuration information from /etc/courier/imapd.cnf. So, after customizing that file (in particular, pay close attention to the common name (CN) parameter, which must match the name of the server the users will connect to), run mkimapdcert(8) to generate the certificate:

# /usr/local/sbin/mkimapdcert
[ ... ]

Now we only have to start the daemons:

# mkdir -p /var/run/courier{,-auth}/
# /usr/local/sbin/authdaemond start
# /usr/local/libexec/imapd.rc start
# /usr/local/libexec/imapd-ssl.rc start

configure the system to start Courier-IMAP on boot:

/etc/rc.conf.local
pkg_scripts="mysqld postfix courier_authdaemond courier_imap courier_imap_ssl"

...and test our hard work! I suggest using a simple Python script, just to give our weary fingers a break:

IMAP_test.py
#!/usr/bin/env python

import imaplib

# Constants
IMAP_SRV = "mail.kernel-panic.it"
USER     = "d.mazzocchio@kernel-panic.it"
PASSWD   = "danix"

# Connect to server
imap_srv = imaplib.IMAP4(IMAP_SRV)
imap_srv.login(USER, PASSWD)

# Select the INBOX folder
imap_srv.select()

# Retrieve message list
msg_nums = imap_srv.search(None, 'ALL')[1]

# Print all messages
for num in msg_nums[0].split():
    msg = imap_srv.fetch(num, '(RFC822)')[1]
    print 'Message %s\n%s\n' % (num, msg[0][1])

# Disconnect from server
imap_srv.close()
imap_srv.logout()

5.2 Adding POP3 access

It is usually desirable that email users be offered the choice between IMAP and POP3 remote access; after all, POP3 users tend to use less disk space, bandwidth and resources on the server.

Adding POP3 support to our mail server is fairly simple; first, we need to add the appropriate package:

Then, we have to run mkpop3dcert(8) to generate the SSL certificate for POP3 over SSL (similarly to mkimapdcert(8), SSL parameters are read from a configuration file, /etc/courier/pop3d.cnf) and start the daemons:

# /usr/local/sbin/mkpop3dcert
[ ... ]
# /usr/local/libexec/pop3d.rc start
# /usr/local/libexec/pop3d-ssl.rc start

Add the following lines to /etc/rc.local(8) to start the POP3 server on boot:

/etc/rc.conf.local
pkg_scripts="mysqld postfix courier_authdaemond courier_imap courier_imap_ssl courier_pop3 courier_pop3_ssl"

Finally, we can perform a quick test to make sure everything works as expected:

# telnet mail.kernel-panic.it 110
Trying 172.16.240.150...
Connected to mail.kernel-panic.it.
Escape character is '^]'.
+OK Hello there.
user d.mazzocchio@kernel-panic.it
+OK Password required.
pass danix
+OK logged in.
list
1 2531
[ ... ]
quit
+OK Bye-bye.
Connection closed by foreign host.
#

5.3 Managing disk space

Quotas allow you to specify the maximum size of maildirs, in order to prevent the /var/vmail filesystem from filling up. The best option would certainly be to use the operating system's built-in quota support, but we can't, because we have a single user writing to all the maildirs; therefore, we must rely on the mail software to enforce quotas on maildirs.

Courier-IMAP comes with built-in quota support, but this solves only one half of the problem: in fact, also Postfix must be able to reject mail sent to over-quota users. To this end, we will rely on the deliverquota(8) utility, which delivers mail taking into account any software-imposed quota on maildirs.

The first step is assigning a quota to each maildir with maildirmake(1). E.g.:

# /usr/local/bin/maildirmake -q 10000000S /var/vmail/kernel-panic.it/d.mazzocchio
# chown vmail /var/vmail/kernel-panic.it/d.mazzocchio/maildirsize

The above command installs an (approximately) 10MB quota on the /var/vmail/kernel-panic.it/d.mazzocchio maildir. Note: maildirmake(1) also allows you to create and initialize maildirs, thus allowing users to access them; otherwise, a user's maildir will be created upon receiving his first email.

Next, we need to define, in /etc/postfix/master.cf(5), a special Postfix daemon for delivery through deliverquota(8):

/etc/postfix/master.cf
[ ... ]
qdeliver  unix  -       n       n       -       -       pipe
  flags=uh user=vmail argv=/usr/local/bin/deliverquota -c -w 90 /var/vmail/${domain}/${user}

and tell Postfix to use this daemon for final delivery to virtual domains, by setting the value of the virtual_transport parameter in /etc/postfix/main.cf:

/etc/postfix/main.cf
virtual_transport = qdeliver

deliverquota(8) will place a warning message into the maildir if, after the message is successfuly delivered, the maildir is at least 90 percent full (-w 90). The body of the warning message is copied verbatim from the /etc/courier/quotawarnmsg file.

Please note that, as reported by Giovanni Bechis, deliverquota(8) fails to correctly deliver emails sent to an alias that maps to multiple accounts, one of which has the same name as the alias itself, unless you set the following parameters in /etc/postfix/main.cf:

/etc/postfix/main.cf
qdeliver_destination_concurrency_limit = 1
qdeliver_destination_recipient_limit = 1 

Note: setting these parameters to "1" disables parallel deliveries to the same recipient.