You are not logged in.

#1 2016-04-05 13:59:51

Registered: 2013-04-19
Posts: 108

DKIM signing with mutt's $sendmail and python-dkim

Usually the place to set up DKIM signing is the SMTP server you send your email from.

However, if you prefer to send using sendmail/msmtp/etc from your local system, or if the upstream SMTP server doesn't support DKIM signing because the hoster just sucks, and still would like to get email sent from your own domain verified by way of DKIM, signing your email locally before passing it on to the upstream SMTP server is a viable and simple solution.

This won't work well if the relaying SMTP server mangles the message body – while the signature would have fulfilled its purpose: indicating that the message has been modified, sending out invalidated email all the time is not the point of signing. So you want to verify that before implementing the steps below.

Some prerequisites:

1. You have set up a DKIM DNS record at $selector._domainkey.$yourdomain and published it.

2. You have the private half of the signing key at hand.

The idea is to insert an additional filter between your favourite sendmail program and the MUA, which in this case is mutt. We will write a simple script which signs the message received from the MUA and then passes it on to the actual sendmail program.

As the signing program, we will be using python-dkim's Any other signing program will do as well; however I found e.g. that opendkim-testmsg from the opendkim package doesn't produce valid signatures and has the rsa-sha1 digest hardcoded – don't use it.

Here's a simple DKIM-signing filter program:


set -e

readonly sendmail=/usr/bin/msmtp
readonly tempdir=$(mktemp -d)

  printf -- "%s: %s" "${0##*/}" "$@" >&2

  rm -r -- "$tempdir"

if [[ -d $tempdir ]]; then
  trap hook_cleanup EXIT
  log_err "Failed to create temporary directory"
  exit 1

cat /dev/stdin > "$tempdir"/original.txt

# Sign messages from 
if egrep -- "^From:.*@YOURDOMAIN" "$tempdir"/original.txt; then YOURSELECTOR YOURDOMAIN PRIVATE_DKIM_KEY \
    <"$tempdir"/original.txt >"$tempdir"/output.txt 2>/dev/null
  cp -- "$tempdir"/original.txt "$tempdir"/output.txt

cat -- "$tempdir"/output.txt | "$sendmail" "$@"

exit $?

I chose a rudimentary approach for determining the signing domain because I preferred not to deal with the subtleties of From: field formatting and email headers yet.

After signing the message, or just doing nothing if the signing condition is not met, we just pipe everything to the sendmail program along with all positional arguments as required by mutt:

              Type: path
              Default: “/usr/sbin/sendmail -oem -oi”

              Specifies the program and arguments used to deliver mail sent by Mutt.  Mutt expects that the specified program interprets additional arguments as recipient addresses.

Accordingly, putting

set sendmail = "/usr/bin/dkim-smtp"

with the path to our script into ~/.muttrc makes the filter work transparently.

I've been using this for a couple of days now and it works well.

Last edited by 2ion (2016-04-05 14:00:06)


Board footer

Powered by FluxBB