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.
Wrote a forum post about compilation problems into FreeBSD forum at https://forums.freebsd.org/threads/opensmtpd-dkim-filter.89789/
However, @palant I do experience a weird issue where it seems that DKIM signature is not calculated correctly (Gmail thinks it is not valid). Sending e-mails with other content from the same e-mail address seem to be correct so I suspect that it has to be something with the contents of that particular e-mail. I can reproduce the problem 100%. This is a worrysome behavior. I can provide raw contents of the e-mail via some private means if that helps you to troubleshoot/reproduce the issue.