Important
Current version of this code has moved into a proper GitHub repository: https://github.com/palant/opensmtpd-filters
The OpenSMTPD documentation currently suggests using either opensmtpd-filter-dkimsign
or opensmtpd-filter-rspamd
for DKIM support. The former lacks functionality and requires you to compile code from some Austrian web server yourself. The latter is overdimensioned for my needs. So I’ve written my own fairly simple filters in Python.
These filters require Python 3 with dkimpy module installed. You can optionally install pyspf module as well, if you want dkimverify.py
to perform SPF verification as well.
Your smtpd.conf
file should contain directives like the following:
filter dkimverify proc-exec "/usr/local/bin/dkimverify.py example.com"
filter dkimsign proc-exec "/usr/local/bin/dkimsign.py example.com:mydkim:/etc/mail/dkim/mydkim.key"
listen on eth0 tls filter dkimverify
listen on eth0 port 587 tls-require auth filter dkimsign
This sets up dkimverify
filter for port 25 (incoming mail) and dkimsign
filter for port 587 (outgoing mail).
dkimverify.py
takes a single command line parameter: the host name to appear in the Authentication-Results
email header. It will add a header like Authentication-Results: example.com; dkim=pass; spf=fail (sender is example.com/1.2.3.4) [email protected]
to emails, this header can then be considered in further processing.
dkimsign.py
takes one or multiple parameters of the form domain:selector:keyfile
on the command line. Instead of configuring all domains on the command line, you can also pass this script -c /etc/mail/dkim/dkim.conf
parameter, with the file /etc/mail/dkim/dkim.conf
containing domain configurations in the same format, one per line.
The opensmtpd.py
module here allows implementing OpenSMTPD filters easily. It is used like following:
from opensmtpd import FilterServer
server = FilterServer()
server.register_handler('report', 'link-auth', handle_auth)
server.register_handler('filter', 'connect', handle_connect)
server.serve_forever()
def handle_auth(session, username, result):
if result == 'pass':
print('Session {} authenticated'.format(session), file=sys.stderr)
def handle_connect(session, rdns, fcrdns, src, dest):
if fcrdns == 'pass':
return 'proceed'
else:
return 'junk'
See smtpd-filters man page for the description of the existing report events and filter requests and their parameters. The FilterServer
class also exposes a convenience method register_message_filter()
that allows filtering complete email messages:
server.register_message_filter(handle_message)
def handle_message(context, lines):
return map(lambda line: line.replace('xyz', 'abc'), lines)
There is also method track_context()
. If called during registration phase, the server will create a context object for each session and pass it to the handlers instead of the session ID.
@jeremyp3: These scripts are still running on my server, and I’ll fix a bug if you find one. But the DKIM verification is being done by the dkim module, not by this script. So it’s rather unlikely that you will find the source of the problem here.