Unwrapping the Courier Filter Process

Courier Filter Process FlowchartThe Courier Mail Server comes along with some quite powerful filter and post-processing mechanisms. It turned out that the rather complex structure requires detailed knowledge about the possibilities of each step to figure out where to apply certain filtering or post-processing tasks for incoming emails. The overview I gained over the mechanisms during my approaches to solve the following tasks – First to filter incoming emails according to common DNS blacklists for certain accounts as well as alias addresses that are just forwarded, and Secound to automatically encrypt every incoming email for certain accounts – should be roughly summarized in this post.

The filter process can be divided into the filtering during receiving the email – at this point the filter’s main task is to decide whether to accept the email or drop the connection right away – and the post-processing of the email after fully receiving it. For the latter, the possibilities are basically endless, as this step may be carried out by any local Mail Delivery Agent (MDA), which is specified in the DEFAULTDELIVERY variable in /etc/courier/courier. In this case maildrop is used as the MDA.

In the first step the BLACKLISTS are queried and the results are reported by setting the specified environment variables. In the default example:

BLACKLISTS='-block=zen.spamhaus.org,BLOCK2 -block=cbl.abuseat.org,BLOCK2'

Spamhaus is queried and the BLOCK2 environment variable is set if the senders IP is listed. If the BLOCK environment variable is specified instead, esmtps immediatly drops the connection and refuses to receive the email. If this behaviour is too strict and one would additionally like to log the mails dropped, one may set any other unused environment varible, in most examples BLOCK2 is used.

The courierfilter supplies an API to external applications that may be enabled in /etc/courier/enablefiltering for specified receive channels, and soft-linked via the filterctl command to /etc/courier/filter/active/. As I have never used this mechanism as it is said to be highly buggy, I would recommend the official documentaion [1].

In the following esmtpd checks whether /etc/courier/maildropfilter exists. This file should contain the path to the maildrop binary that is included in the courier distribution, in this case it is /usr/bin/maildrop. If this file exists maildrop is executed in the embedded mode. In this mode the functionality of maildrop is somewhat limitted and some commands, such as xfilter, are not available. See [2] for a full list. However there is a workaround for this limitation, as it is possible to include any mailfilter script that resides within /etc/courier/maildroprcs/ for which the limitations are suspended [2]. In this step the file $HOME/.mailfilters/rcptfilter is evaluated first, right after receiving the header of the email. If this filter returns with EXITCODE = 0 the email is accepted and the transfer of the DATA section is continued. If the EXITCODE = 99 the transfer of the DATA section is also continued but before it is acknowledged $HOME/.mailfilters/smtpfilter is evaluated and the email is only accepted if it returns EXITCODE = 0. In any other case the connection is dropped. At this point the BLOCK2 environment variable may be queried using the import statement. For alias addresses specified in /etc/courier/aliases/ that are just forwarded to a local account or an external address, there is usually no proper $HOME specified. In this case /etc/courier/aliasfilteracct may specify a $HOME for alias addresses. The $HOME given in /etc/courier/aliasfilteracct must be a valid $HOME of an existing user (no virtual user), otherwise courier will fail to evaluate any rcptfilter and exit with „400 stat() failed on aliasfilteracct“. For aliases only the rcptfilter can be used and files have to be named $HOME/.mailfilters/rcptfilter-alias-name, where name is the full alias address [3]. For local aliases which don’t name a domain /etc/courier/me is appended. Be aware that each period ‚.‘ in name must be replaced by a colon ‚:‘ e.g. rcptfilter-alias-sample@markuspetermann:net. One may also create an rcptfilter-default file which is used if no other file matches the alias. There are quite a few more features, especially regarding sub-addresses that won’t be covered at this point.

After the email has been fully received esmtpd invokes the Mail Delivery Agent, which is specified via the DEFAULTDELIVERY variable in /etc/courier/courierd. The default value points to ./Maildir which just places the email in the Maildir within the users $HOME. If one wishes to implement further post-processing of received emails, one may use maildrop in delivery mode to perform such tasks. This gives us rather endless possibilities to process and modify the incoming email. If maildrop is specified as local MDA it will first evaluate /etc/courier/maildroprc which contains the part of the maildrop script that applies globally to every incoming email. In addition to this one may place a $HOME/.mailfilter which contains any further part of the script that only applies to the emails of the specific user. At this point the BLOCK2 and other environment variables are not available anymore, as this is a completely different instance of maildrop running. Here, one may for example use the xfilter command to encrypt every incoming email (that is unencrypted) with the PGP key of the user, before delivering it.

[1] http://www.courier-mta.org/courierfilter.html
[2] http://www.courier-mta.org/maildrop.html
[3] http://www.courier-mta.org/localmailfilter.html

This article is licensed under CC BY-NC-SA 4.0