Compiling Courier-MTA from sources on Debian buster/sid

Updated on June 26, 2019, for the upcoming Debian GNU/Linux Buster release.

This aims to be a complete, but not necessarily friendly guide, at least as of today. It contains a list of commands and actions I used to compile Courier-MTA from sources on a very basic Debian stable/testing/sid instance (e.g. a mix of stable, testing and sid).

Why? Because current Courier-MTA packages in Debian are nearly unmantained. I’m sure Markus Wanner (current mantainer) is doing his best and I thank him for his work, but I need a way out, should Courier-MTA ever fall orphaned. I’d like to help him or anyone else if I could, but I understand nothing of Debian packaging tecniques, nor Debian packaging policies. I just happen to use Debian because I like it and Courier-MTA for the very same reason, so I better play it safe and learn how to compile Courier-MTA on Debian myself.

Enough words, now the guide:

I started from the Debian setup itself, using a KVM guest. Please adjust things to match your needs (volume group name, logical volume name, ram size, disk size, network bridge name). You can install Debian in many other ways, this is just one of them. I started from the latest stable release (stretch) and added buster and sid afterwards.

root@kvmhost:~# virt-install \
  --name couriermta --ram 2048 \
  --disk vol=tumblevolgroup/vm-couriermta,bus=virtio \
  --vcpus 2 --os-type linux --os-variant generic \
  --network bridge=virbr20,model=virtio \
  --graphics none --console pty,target_type=serial --location \
  http://mirror.switch.ch/ftp/mirror/debian/dists/buster/main/installer-amd64/ \
  --extra-ar gs 'console=ttyS0,115200n8 serial'

When tasksel asked what packages to install, I selected only standard system utilities and SSH server. Then I added my favourite packages after the install:

root@couriermta:~# apt-get install vim vim-tiny- screen ntp

Let’s add sid sources, because I like having recent packages available if needed:

root@couriermta:~# cd /etc/apt/sources.list.d
root@couriermta:/etc/apt/sources.list.d# cat > sid.list << EOF

deb http://ftp.de.debian.org/debian/ sid main contrib
deb-src http://ftp.de.debian.org/debian/ sid main contrib
EOF

But please note that I configure priorities so that my system will prefer stable, and it will install buster/sid packages only if I explicitly ask it to do so:

root@couriermta:/etc/apt/sources.list.d# cd /etc/apt/preferences.d/
root@couriermta:/etc/apt/preferences.d# cat > sid << EOF
Package: *
Pin: release a=stable
Pin-Priority: 990

Package: *
Pin: release a=testing
Pin-Priority: 700

Package: *
Pin: release a=unstable
Pin-Priority: 600
EOF

Next we need to install the gcc compiler and toolchain. While we are at it, we also install some libs that we’ll need later, when we’ll be compiling authlib and Courier-MTA itself. Some of these libraries might already be installed in your system, depending on how you installed Debian and what you choose to install in the tasksel step. If they are, no worries, apt will just skip their installation.

# apt-get update
[...]
# apt-get install build-essential libltdl-dev \
  libgdbm-dev libgdbm-compat4 \
  libpcre3-dev libidn11-dev libidn2-0-dev \
  libgnutls28-dev libgcrypt20-dev libperl-dev gnutls-bin
[...] Do you want to continue? [Y/n]

Time to add a non-root user I’ll use to compile Courier-MTA source code:

root@couriermta:~# adduser couriermta
Adding user `couriermta' ...
Adding new group `couriermta' (1001) ...
Adding new user `couriermta' (1001) with group `couriermta' ...
Creating home directory `/home/couriermta' ...
Copying files from `/etc/skel' ...
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully 
Changing the user information for couriermta
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
root@couriermta:/etc/apt/preferences.d# mkdir \
  -p /opt/courier/sources
root@couriermta:/etc/apt/preferences.d# chown \
  -R couriermta.couriermta /opt/courier/
root@couriermta:/etc/apt/preferences.d# su - couriermta
couriermta@couriermta:~$ cd /opt/courier/sources/
couriermta@couriermta:/opt/courier/sources$

Let the party begin. We download the source code (unicode library, authlib and Courier-MTA itself):

couriermta@couriermta:/opt/courier/sources$ wget 'https://sourceforge.net/projects/courier/files/courier-unicode/2.1/courier-unicode-2.1.tar.bz2'
[...]
couriermta@couriermta:/opt/courier/sources$ wget 'https://sourceforge.net/projects/courier/files/authlib/0.69.0/courier-authlib-0.69.0.tar.bz2'
[...]
couriermta@couriermta:/opt/courier/sources$ wget 'https://sourceforge.net/projects/courier/files/courier/1.0.8/courier-1.0.8.tar.bz2'
[...]
couriermta@couriermta:/opt/courier/sources$ for i in *.bz2 ; do tar xjf $i ; done

We start compiling and installing the unicode library. Please note that I chose to install it in /opt/courier/unicode, but you can choose any other directory that suits your needs. All commands below, until the end of this giude, will assume you chose /opt/courier/unicode, so you might need to customize them too if you choose a different location:

couriermta@couriermta:/opt/courier/sources$ cd courier-unicode-2.1/
couriermta@couriermta:/opt/courier/sources/courier-unicode-2.1$ ./configure --prefix=/opt/courier/unicode
[...]
couriermta@couriermta:/opt/courier/sources/courier-unicode-2.1$ make
[...]
couriermta@couriermta:/opt/courier/sources/courier-unicode-2.1$ su
root@couriermta:/opt/courier/sources/courier-unicode-2.1# make install
[...]
root@couriermta:/opt/courier/sources/courier-unicode-2.1# exit

Now we compile the authentication library. Likewise, I chose /opt/courier/authlib, but it’s up to you.

$ cd /opt/courier/sources/courier-authlib-0.69.0
$ mkdir ../include
$ ln -s /opt/courier/sources/courier-unicode-2.1/ /opt/courier/sources/include/unicode
$ ./configure --prefix=/opt/courier/authlib CFLAGS='-I/opt/courier/sources/include/unicode' LDFLAGS='-L/opt/courier/unicode/lib'
[...]
$ make
[...]
$ su
root@couriermta:/opt/courier/sources/courier-authlib-0.69.0# make install
[...]
root@couriermta:/opt/courier/sources/courier-authlib-0.69.0# exit

We’re all set, we can now compile Courier-MTA itself:

$ cd ../courier-1.0.8/
$ ln -s /opt/courier/sources/courier-authlib-0.69.0/ /opt/courier/sources/include/authlib
$ export COURIERAUTHCONFIG=/opt/courier/authlib/bin/courierauthconfig 
$ export CPATH=/opt/courier/sources/include/authlib:/opt/courier/sources/include/unicode
$ ./configure --prefix=/opt/courier/courier --with-gnutls --with-notice=unicode CFLAGS="-I/opt/courier/sources/include/authlib -I/opt/courier/sources/include/unicode" LDFLAGS="-L/opt/courier/authlib/lib/courier-authlib -L/opt/courier/unicode/lib"
[...]
$ make
[...]
$ make check
[...]
$ su
root@couriermta:/opt/courier/sources/courier-1.0.8# make install
[...]
root@couriermta:/opt/courier/sources/courier-1.0.8# make install-configure
[...]

And that’s all about it, meaning Courier-MTA is compiled, assuming you got no error messages with the commands above. However barely compiling it is of no use if we don’t actually run it,  and, before running it, we need a bit of configuration. Let’s add some essential paths to out PATH variable:

# echo 'export PATH=$PATH:/opt/courier/courier/bin:/opt/courier/courier/sbin' >> $HOME/.bashrc

There seemed to be a bug in the imapd script, at least in the August 2018 development release that I used for the first version of this document. Now it seems fixed, anyway I keep this here for a while until I’m sure it’s not needed anymore. The imapd script did not correctly set permissions on the TLS_CACHEFILE. If that happens to you, either manually fix permissions:

# chmod g+rw \
  /opt/courier/courier/var/couriersslpop3cache
# chgrp daemon \
  /opt/courier/courier/var/couriersslpop3cache

or manually fix the script:

# vi /opt/courier/courier/share/imapd
replace @authmailuser@ and @authmailgroup@ 
with "daemon", save and exit.

I won’t delve into details of configuring and running Courier-MTA here, because you can find extensive documentation about it at the Courier-MTA website, and there’s nothing Debian specific in that procedure.  However I feel like giving a few final hints.

if you are running a KVM guest in a NAT-ted network of virtual servers, which happens to be my setup, don’t forget to limit the smtpaccess that comes by default with Courier-MTA:

# vi /opt/courier/courier/etc/smtpaccess/default

Comment out the following lines, that are line 13 and line 14 in the release of Courier-MTA I downloaded:

10 allow,RELAYCLIENT
192.168 allow,RELAYCLIENT

Those lines should look like this:

#10 allow,RELAYCLIENT
#192.168 allow,RELAYCLIENT

Then run, as root

# makesmtpaccess

That will avoid a open relay inside the virtual servers network (and also outsite it, if you ever map the public IP 25/tcp port to your Courier-MTA instance).

If you plan to use SSL/TLS enctypted connections (and you SHOULD!), you need to provide Courier with valid certificates. You can use LetsEncrypt to obtain valid certificates for your serve for free, but, before that, you may need at least a test (hence not valid) certificate, just to check encrypted connections are actually working.

The mkimapdcert script can be used to create such test certificate. Just run it and you’re done. You can now test your IMAP server with any client: just make sure you tell it to accept your invalid certificate.

That’s all, you can now continue reading the official documentation from the “Installation” chapter on, which will work without modifications in Debian too.

After you have configured Courier basic settings, you may need a handy script to start, stop and gracefully restart Courier. Here’s mine /opt/courier/scripts/courier.sh:

#!/bin/bash

/opt/courier/authlib/sbin/authdaemond $1
/opt/courier/courier/sbin/courierfilter $1
/opt/courier/courier/sbin/courier $1
/opt/courier/courier/sbin/esmtpd $1
/opt/courier/courier/sbin/esmtpd-msa $1

Then I added a /etc/rc.local file containing

#!/bin/bash
/opt/courier/scripts/courier.sh start

So far so good. However if you reached this point, you’ll likely want to send emails too. Nowadays sending emails can be a tough job. Times when you could script telnet to forge a sender address and drop messages into random mailboxes are over. A default Courier setup is far better than that, but it does not include DKIM signatures. Please note, DKIM is not mandatory, but its adoption is rising and you might want to jump on that bandwagon too. So let’s install some packages required to compile and run zdkimfilter:

# apt-get install libopendkim-dev opendkim opendkim-tools libunistring-dev

Then we download and compile zdkimfilter. We need to tell configure where to find the courer-config binary and specify a fake courier version manually (the latter because of a bug in version 1.6 of zdkimfilter):

# su - couriermta
$ mkdir /opt/courier/sources/zdkimfilter/
$ cd /opt/courier/sources/zdkimfilter/
$ wget 'https://www.tana.it/sw/zdkimfilter/zdkimfilter-1.6.tar.gz'
$ tar xzf zdkimfilter-1.6.tar.gz
$ cd zdkimfilter-1.6/
$ ./configure --with-courier-version=60 --prefix=/opt/courier/zdkimfilter COURIER_CONFIG=/opt/courier/courier/bin/courier-config
$ make
$ su
# make install

All the rest is up to you and not documented here, e.g. adding SPF and DKIM DNS records, and DMARC policies as well. You can find extensive documentation about those out there on the internet. The same is true for zdkimfilter configuration too, but I like to share this last bit, which is a script I use to enable DKIM for a particular domain:

#!/bin/bash
#
# Simple script to create DKIM keys for Courier/zdkimfilter.
# Copyright © 2017 Lucio Crusca <lucio@sulweb.org>
# No warranty whatsoever. 
# This is PUBLIC DOMAIN code: you can use it for whatever you want, except for 
# holding me responsible for anything. If you choose to use this code, you agree
# to take full responsibility for your actions.
#
# End of legal nonsense

HEADEND=$(grep -n 'End of legal nonsense' "$0" | head -n 1 | cut -d':' -f1)
HEADEND=$(( ${HEADEND} - 1))
cat "$0" | head -n ${HEADEND} | tail -n $(( ${HEADEND} - 1 )) | sed -e 's/^#//'

DOMAIN="$1"
COURIERCONF=/opt/courier/courier/etc
KEYSDIR="${COURIERCONF}/filters/keys"

if [ "${DOMAIN}" == "" ] ; then
  echo "Please specify the domain for the new DKIM key, e.g.:"
  echo
  echo "$0 example.com"
  echo
  exit 1
fi

if ! [ -d "${KEYSDIR}" ] ; then
  echo "${KEYSDIR} is not a directory. Please edit $0 to match your Courier setup."
  exit 2
fi

NOW=$(date +%F+%T | sed -e 's/-//g' | sed -e 's/+//g' | sed -e 's/://g')
SELECTOR="${DOMAIN}${NOW}"

cd "${KEYSDIR}"
if [ -L ${DOMAIN} ] ; then
  echo "DKIM keys seem to already exist for ${DOMAIN}."
  echo "You may replace them if you want, further confirmation will be asked before actual replacement."
  echo -n "Do you want to proceed and create new keys (y/N)? "
  read REPLACE
  if [ "${REPLACE}" != "y" ] && [ "${REPLACE}" != 'Y' ] ; then
    echo "Nothing changed."
    exit 0
  fi
fi

opendkim-genkey -b 4096 -d "${DOMAIN}" -D "${KEYSDIR}" -s ${SELECTOR} -r nosubdomains -v
chmod 640 ${KEYSDIR}/${SELECTOR}.*
chown root.daemon ${KEYSDIR}/${SELECTOR}.*
echo "DKIM Keys created."
echo "Please add the following TXT record to ${DOMAIN} domain and let it propagate."
echo
cat ${SELECTOR}.txt | sed -e "s/^${DOMAIN}//" cat ${SELECTOR}.txt echo if [ "${REPLACE}" != "" ] ; then echo "Since you have choosen to replace the current DKIM keys, you need to decide when the new keys will be activated." echo "By now your system is still using the current keys to sign messages." while [ "${ACTIVATENOW}" != "NOW" ] ; do echo -n "Please type NOW (uppercase) followed by Return to replace the current keys with the new ones: " read ACTIVATENOW if [ "${ACTIVATENOW}" != "" ] && [ "${ACTIVATENOW}" != "NOW" ] ; then echo "You need to type NOW. You may also hit Ctrl+C to break the script and handle the activation manually." fi done rm -f "${DOMAIN}" fi ln -s "${SELECTOR}.private" "${DOMAIN}"