Sync’ up! … without getting drained

mar 25

OpenSMTPD & Maildrop working in concert

OpenBSD has many ways of filtering mails based on criteria right out of the box. But sometimes, the workload you want to put on your incoming mail is better suited for tools designed just for that.

Long ago, procmail dominated this space. But since the project’s last commit was September 10th, 2001, it’s better to reach for tools that are still active.

Maildrop

The Courier Mail Server comes with many programs, and thankfully, if you already have an MTA that you lean on but still wanted to use some of their fancy tooling, that will be fine.

In our case, we wanted to use maildrop(5), which when installed on OpenBSD, comes with a handful of filtering options, the Mail Delivery Agent (MDA), some MIME building programs, etc. Installing is as follows:

# pkg_add maildrop # N.B. the non-postfix package will do

We like to use maildrop(5) in ‘delivery’ mode, whereby the MDA listens on a socket, and you can start, stop, restart it like any other service. You can play with maildrop(5) in manual mode, or the third option: embedded mode.

What we really liked about this project was how opinionated it was, how well the documentation was crafted, and how packed the mailing list was with years of helpful solutions.

However, little was out there in the form of a quick and dirty guide to get everything humming along. Which, to be honest, has become a modern-day pattern for how to do things. Furthermore, AI chatbots were very unhelpful and hallucinated more than normal.

Light configuration

Because maildrop(5) is so opinionated, the following configuration are not just suggestions, they are the rule (for the most part). If a certain permission isn’t just so, maildrop(5) won’t run. This is for security reasons, and we can really get behind that.

In ‘delivery’ mode, your regular user needs a ‘~/.mailfilter’ file with ‘0600’ permissions. The naming is not mutable, at least out of the box. Also, this file needs to be owned by said user.

This is the ‘filter’ for your mail, and it can be very complex. It can perform things as trite as a ‘vacation reply,’ to email munging — like what we are up to over at Whenhen.com. The maildropfilter(7) is a whole Turing-complete language, so be confident that it will meet your every need.

Here is a plain-jane ‘.mailfilter’ configuration that just matches on a ‘From’ email, and based on that, files it into a Maildir directory found in the home directory named ‘.vip’ :

if (/^From:.*vip@foo.com/)
{
    to .vip
}

Here, maildrop(5) again will complain loudly if this ‘.vip’ isn’t a bone fide Maildrop directory. So set it up as it expects as follows:

cd ~
mkdir -p .vip/{cur,new,tmp}

While we’re at it, create your regular Maildir (for all other mail) in your home directory named ‘.maildir’ as well.

mkdir -p .maildir/{cur,new,tmp}

When maildrop(5) is running in ‘delivery’ mode, the option to debug has to be set up via the system config file, ‘/etc/courier/authdaemonrc’. While a ‘-V K’ flag is available for the other modes, it isn’t a valid one in ‘delivery’ mode. Ergo the ‘rc’ file. This file is also strictly a ‘0600’ file with ‘root:wheel’ ownership.

You can make your debugging as chatty as you’d like with this ‘rc’ file, or, like us, you can test things in ‘manual’ mode till everything is working smoothly. In ‘delivery’ mode, with debugging on, you can follow the messages with the usual tail -f /var/log/messages.

Preparing the daemon

The maildrop(5) daemon is called ‘authdaemond’ and lives in ‘/usr/local/sbin’. Yes, it’s named ‘Daemon Daemon.’ There’s a first for everything!

It runs as root, and writes a ‘pid’ file to ‘/var/run/courier-auth’, so better make this directory before you forget and maildrop(5) complains about it:

# mkdir /var/run/courier-auth

This can be tweaked in your ‘/etc/courier/authdaemonrc’ file, but there’s nothing wrong with the default.

Lastly, a dirty solution to start the daemon at startup can be achieved by prepending a line to root’s crontab(1), as follows:

@reboot /bin/sh /usr/local/sbin/authdaemond start
...

Some interesting information is available from the install ‘README’ here.

SMTPD configuration

Thankfully, the OpenBSD folks have made working with your custom MDAs painless. There’s a first class way of going about it. For example:

...
action "vip" mda "/usr/local/bin/maildrop -d %{dest.user:lowercase}"

match from any mail-from "vip@foo.com" for any action "vip"
...

This sets up a rule that mail coming from a certain someone, will get handled by maildrop(5). It’s even a tad redundant, having a filter, before a filter, but it’s up to you to decide what gets sent to ‘.maildir’, what gets sent to maildrop(5), etc.

Here is a fleshed-out example that sends local mail to ‘/var/mail/user’, all Internet mail to ‘~/.maildir’, and ‘vip’ mail to ‘~/.vip’:

pki mail_cert cert "/etc/ssl/mail.bar.com.fullchain.pem"
pki mail_cert key  "/etc/ssl/private/mail.bar.com.key"

# N.B. newaliases(8)
table aliases db:/etc/mail/aliases.db

# N.B. filters
filter "dkimsign_rsa" proc-exec "filter-dkimsign -d bar.com -s selector1 \
    -k /etc/mail/dkim/private.rsa.key" user _dkimsign group _dkimsign

# N.B. local incoming
listen on lo0 filter "dkimsign_rsa"
# N.B. internet incoming
listen on socket filter "dkimsign_rsa"
# N.B. handle *outgoing* internet mail
# no auth needed as we are not offering smtp
listen on egress tls pki mail_cert

action "local_mail_with_aliases" mbox alias 
# N.B. handle vip mail
action "vip_internet_mail_without_aliases" mda \ 
  "/usr/local/bin/maildrop -d %{dest.user:lowercase}"
action "internet_mail_without_aliases" maildir \ 
  "/home/%{dest.user:lowercase}/.maildir"
action "outbound" relay

# N.B. handle internet mail (for vip and domain)
match from any mail-from "vip@foo.com" for any action \ 
  "vip_internet_mail_without_aliases"

# N.B. handle internet mail (for domain)
match from any for domain "bar.com" action "internet_mail_without_aliases"

# N.B. handle local mail
match from local for local action "local_mail_with_aliases"

match from local for any action "outbound"

A number of these configurations can be understood with the aid of the many examples of ‘setting up a mail server in OpenBSD’ that are abound. But this ‘smtpd.conf’ file is pretty typical.

Test-driving maildrop(5)

If everything was configured with care, then our maildrop(5) service can be started, smtpd(8) checked and restarted, and we can do some testing.

# sh /usr/local/sbin/authdaemond start
# smtpd -n
# rcctl restart smtpd

By sending mail from ‘vip@foo.com,’ ‘alice@foo.com’ and sending mails around locally to and from regular users, you should see the appropriate spool mail, and Maildir in your home directories populate as expected.