Docu review done: Tue 17 Oct 2023 10:50:14 AM CEST

Table of content

General

In this documentation, we assue that you have already a running and working postgrey instance on your server.

Installation

First you need fo course the packages installed, for Debian, you can use the command below.

$ apt install opendkim opendkim-tools

Preperation

Next is to create the dircetories where you want to store your configurtion files and keys and set the correct owner and group + permissions.

mkdir -p /etc/opendkim/keys
chown -R opendkim:opendkim /etc/opendkim
chmod go-rw /etc/opendkim/keys

Assuming you have the above mentined packages already installed and running Debian (or a Debian based system), you should add the user postgres to the group opendkim.

Configuration

OpenDKIM Config

Now you need to configure your /etc/opendkim.conf file.

This is how a configuration could look like:

# This is a basic configuration for signing and verifying. It can easily be
# adapted to suit a basic installation. See opendkim.conf(5) and
# /usr/share/doc/opendkim/examples/opendkim.conf.sample for complete
# documentation of available configuration parameters.

Syslog              yes
SyslogSuccess       yes

# Common signing and verification parameters. In Debian, the "From" header is
# oversigned, because it is often the identity key used by reputation systems
# and thus somewhat security sensitive.
Canonicalization    relaxed/simple
Mode                sv
SubDomains          no
AutoRestart         yes
AutoRestartRate     10/1M
Background          yes
DNSTimeout          5
SignatureAlgorithm  rsa-sha256
OversignHeaders     From

# In Debian, opendkim runs as user "opendkim". A umask of 007 is required when
# using a local socket with MTAs that access the socket as a non-privileged
# user (for example, Postfix). You may need to add user "postfix" to group
# "opendkim" in that case.
UserID          opendkim
UMask           007

# Socket for the MTA connection (required). If the MTA is inside a chroot jail,
# it must be ensured that the socket is accessible. In Debian, Postfix runs in
# a chroot in /var/spool/postfix, therefore a Unix socket would have to be
# configured as shown on the last line below.
Socket          local:/var/spool/postfix/opendkim/opendkim.sock

PidFile         /run/opendkim/opendkim.pid

# The trust anchor enables DNSSEC. In Debian, the trust anchor file is provided
# by the package dns-root-data.
TrustAnchorFile     /usr/share/dns/root.key

# Map domains in From addresses to keys used to sign messages
KeyTable           refile:/etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# Hosts to ignore when verifying signatures
ExternalIgnoreList  /etc/opendkim/trusted.hosts

# A set of internal hosts whose mail should be signed
InternalHosts       /etc/opendkim/trusted.hosts

Defaults for opendkim service

For Debian and Debian based systems, you should also configure the file /etc/defaults/opendkim. Sample:

# NOTE: This is a legacy configuration file. It is not used by the opendkim
# systemd service. Please use the corresponding configuration parameters in
# /etc/opendkim.conf instead.
#
# Previously, one would edit the default settings here, and then execute
# /lib/opendkim/opendkim.service.generate to generate systemd override files at
# /etc/systemd/system/opendkim.service.d/override.conf and
# /etc/tmpfiles.d/opendkim.conf. While this is still possible, it is now
# recommended to adjust the settings directly in /etc/opendkim.conf.
#
#DAEMON_OPTS=""
# Change to /var/spool/postfix/run/opendkim to use a Unix socket with
# postfix in a chroot:
RUNDIR=/var/spool/postfix/opendkim
#RUNDIR=/run/opendkim
#
# Uncomment to specify an alternate socket
# Note that setting this will override any Socket value in opendkim.conf
# default:
SOCKET=local:$RUNDIR/opendkim.sock
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=

Excludes for Signing

Sometimes you don’t want to sign a mail, e.g. if you are sending the mail only internal. To do this, you have to add them into the file /etc/opendkim/trusted.hosts or better say, in the file which you have configured within the option InternalHosts.

10.0.0.0/8
127.0.0.1
::1
localhost
itgui.de
*.itgui.de

Key pairing

To configure which key should be use for which (sub)domain, you need to add this information into the file /etc/opendkim/signing.table or better say, in the file which you have configured within the option SigningTable like this:

*@itgui.de    default._domainkey.itgui.de

If you have more keys for other (sub)domains, just add a new line and specify there the mapping.

Just make sure that you have only one mapping per line.

Next is to link the link now virtual name with the real flie. This is done in the file /etc/opendkim/key.table or better say, in the file which you have configured within the option KeyTable like this:

default._domainkey.itgui.de     itgui.de:default:/etc/opendkim/keys/itgui.de/default.privat

Kreating key pair

To create the key pair, you can use the command opendkim-genkey, like this (+ ensure the permissions):

$ opendkim-genkey -b 2048 -d "itgui.de" -D "/etc/opendkim/keys/itgui.de" -s default
$ chown -R opendkim. /etc/opendkim/keys/itgui.de
$ chmod -R o-rw /etc/opendkim/keys/itgui.de

In general it is recommended to renew the keys every 4 months. But if you also look at the big players, they also keep sometimes the for years ;)

If you are rotating keys, keep in mind, mails can keep alive for very long times, so ensure that you have both keys (old and new one) available for quite some time in DNS records to ensure, that mails signed with old keys, can be still validated.

Placing the DNS record

Now you need to place your public key into your DNS as a TXT record.

First you need the data what you want to place, basically it is everything which is between the breaks ( ), but here is a oneline for it (just a sample output) :

$ echo "v=DKIM1; k=rsa; s=email; $(sed -E '/default._domain.*IN.*TXT.*DKIM/d;s/\t*  *"//g;s/".*//g' "/etc/opendkim/keys/itgui.de/default.txt")"
v=DKIM1; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA...73Qzl5Czna797955/zX7Bp10e/lATZbVtP6Qu6eC2TMpWx06bEDRZ...oAtNNuhQIDAQAB

If you are able to talk to your DNS via API, you can easily put that into an automation, like this for netcup.de DNS:

#!/bin/bash
dkim_domain="${1}"
dkim_pre="v=DKIM1; h=sha256; k=rsa;"

dkim_dns_record_id=$(/usr/local/scripts/ncdapi.sh -g "${dkim_domain}" | jq -r '.[] |  select ( .destination | contains("DKIM")) | .id')
if grep -qE '[0-9]{8,}' <<<"${dkim_dns_record_id}" ; then
    echo "Detected existing DKIM record (id: ${dkim_dns_record_id}), skipping process..."
    exit 0
fi

dkim_key=$(sed -E '/default._domain.*IN.*TXT.*DKIM/d;s/\t*  *"//g;s/".*//g' "/etc/opendkim/keys/${dkim_domain}/default.txt" | tr -d '\n')

if [ -z "${dkim_key}" ] || grep -vqE "^p=" <<<"${dkim_key}"; then
    echo "Failed during parsing, skipping process..."
    exit 1
fi

if /usr/local/scripts/ncdapi.sh -N default._domainkey "${dkim_domain}" TXT "${dkim_pre} ${dkim_key}" ; then
    echo "DNS record added"
else
    echo "Failed to insert DNS record"
    exit 1
fi

If not, then create it with the following specs (adopted to your data of course):

  • Host: default._domainkey
  • Domain: itgui.de
  • Type: TXT
  • Destination: v=DKIM1; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA...73Qzl5Czna797955/zX7Bp10e/lATZbVtP6Qu6eC2TMpWx06bEDRZ...oAtNNuhQIDAQAB

Restart systemd service

Now we have everything on the server in place and can finaly restart the opendkim serivce

$ systemctl restart opendkim.serivce

Testing your setup

Of course you want to konw if it is working, and with the command opendkim-testkey you can do that.

opendkim-testkey -d itgui.de -s default -vvv
  opendkim-testkey: using default configfile /etc/opendkim.conf
  opendkim-testkey: checking key 'default._domainkey.itgui.de'
  opendkim-testkey: key not secure
  opendkim-testkey: key OK

Dont worry about the key not secure this is just that DNSSEC is not in use right now and you can continue to work with it.

Postfix setup

Now we want to enable it in postfix.

Before you enable it, set the option soft_bounce=yes in your /etc/postfix/main.cf.

By setting this option, you don’t loose any mails if something goes wrong in your setup and postfix will just respond with a 4xx error to the sender mail server, instead of 5xx error, which leads to, that the sender mail server will retry it in a couple of minutes again.

Inside the /etc/postfix/main.cf you have to add the following to enable the opendkim:

# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

If you have already something in your milter (e.g. spamassassin), then just add it like this:

smtpd_milters     = unix:spamass/spamass.sock, local:/opendkim/opendkim.sock
non_smtpd_milters = local:/opendkim/opendkim.sock

Now you have to restart the postfix service like this for example systemctl restart postfix.serivce.

Mailtest

Now you can try to send your self a mail, after that have a look at the mail headers and you should find something like this:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=itgui.de;
    s=default; t=1479644038;
    bh=g3zLYH4xKxcPrHOD18z9YfpQcnk/GaJedfustWU5uGs=;
    h=To:From:Subject:Date:From;
    b=VI6TIDLrzG8nAUYWwt5QasKJkkgU+Sv8sPGC1fynSEGo0GSULGgCjVN6KXPfx1rgm
     1uX2sWET/oMCpxjBFBVUbM7yHGdllhbADa2SarzYhkoEuNhmo+yxGpXkuh0ttn4z7n
     OmkLwjrdLafMOaqSBrXcTMg/593b/EQXtouEvHqOG59d0fubIGCJBF6DG5gcITS9CP
     q9tiVwIDVuQqynL9Crodl+2IObcvl15MeK2ej322qrjrTij6vZOru9SeVno4LNTkQ7
     tOT4s14BGLx8aRe5YXZj38AWsR6DxkT6OzM+3TnOhIfX3ZdkufMz8AUnTNuLhViZ1M
     betE0x1iOi/HQ==

A nice test page for this is isnotspam, before testing, you should wait a bit till the DNS changes has propagaged throught the DNS servers over the wild.

Issues

Double sigend mail

If you are running postfix, spamassassin and opendkim for the same mail instance, then it can happen that you see that your outgoing mails are swinged twice by opendkim.

The reason for that is, that they get signed before spamassassins takes care about the mail and after it. In the following logsample, you see the double signing.

May  4 13:37:42 my_mail_server postfix/smtpd[2240071]: connect from myhost[10.0.0.2]
May  4 13:37:42 my_mail_server postfix/smtpd[2240071]: Anonymous TLS connection established from myhost[10.0.0.2]: TLSv...
May  4 13:37:42 my_mail_server postfix/smtpd[2240071]: E242018B0000: client=myhost[10.0.0.2], sasl_method=xxxxx, sasl_username=myuser@itgui.de
May  4 13:37:42 my_mail_server postfix/cleanup[2240086]: E242018B0000: message-id=<a3cdd989-e554-8cc5-1c8d-1e1c5697ae9e@itgui.de>
May  4 13:37:42 my_mail_server opendkim[2240050]: E242018B0000: DKIM-Signature field added (s=default, d=itgui.de)
May  4 13:37:42 my_mail_server postfix/qmgr[3846771]: E242018B0000: from=<myuser@itgui.de>, size=1712, nrcpt=1 (queue active)
May  4 13:37:42 my_mail_server spamd[2644747]: spamd: connection from ::1 [::1]:57232 to port 783, fd 5
May  4 13:37:42 my_mail_server spamd[2644747]: spamd: setuid to spamassassin succeeded
May  4 13:37:42 my_mail_server spamd[2644747]: spamd: processing message <a3cdd989-e554-8cc5-1c8d-1e1c5697ae9e@itgui.de> for spamassassin:5000
May  4 13:37:42 my_mail_server postfix/smtpd[2240071]: disconnect from myhost[10.0.0.2] ehlo=2 starttls=1 auth=1 mail=1 rcpt=1 data=1 quit=1 commands=8
May  4 13:37:42 my_mail_server spamd[2644747]: spamd: clean message (-0.8/5.0) for spamassassin:5000 in 0.2 seconds, 2173 bytes.
May  4 13:37:42 my_mail_server spamd[2644747]: spamd: result: . 0 - ALL_TRUSTED,DKIM_INVALID,DKIM_SIGNED,URIBL_BLOCKED scantime=0.2,size=2173,user=spambro,uid=0000,required_score=5.0,rhost=::1,raddr=::1,rport=57232,mid=<a3cdd989-e554-8cc5-1c8d-1e1c5697ae9e@tgui.de>,autolearn=no autolearn_force=no
May  4 13:37:42 my_mail_server postfix/pickup[2021986]: 274A118B0005: uid=0000 from=<myuser@itgui.de>
May  4 13:37:42 my_mail_server postfix/pipe[2240087]: E242018B0000: to=<myuser@itgui.de>, orig_to=<my_second_user@itgui.de>, relay=spamassassin, delay=0.26, delays=0.08/0.01/0/0.18, dsn=2.0.0, status=sent (delivered via spamassassin service)
May  4 13:37:42 my_mail_server postfix/qmgr[3846771]: E242018B0000: removed
May  4 13:37:42 my_mail_server postfix/cleanup[2240086]: 274A118B0005: message-id=<a3cdd989-e554-8cc5-1c8d-1e1c5697ae9e@itgui.de>
May  4 13:37:42 my_mail_server opendkim[2240050]: 274A118B0005: DKIM-Signature field added (s=default, d=itgui.de)
May  4 13:37:42 my_mail_server postfix/qmgr[3846771]: 274A118B0005: from=<myuser@itgui.de>, size=2639, nrcpt=1 (queue active)
May  4 13:37:42 my_mail_server postfix/pipe[2240092]: 274A118B0005: to=<myuser@itgui.de>, relay=dovecot, delay=0.03, delays=0.01/0.01/0/0.02, dsn=2.0.0, status=sent (delivered via dovecot service)

To get rid of the issue, you have to perform a small change in the master.cf of postfix.

Search in the master.cf the line where you have spamassassin configured for the smtp service and add -o receive_override_options=no_milters like this:

# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
  -o content_filter=spamassassin -o receive_override_options=no_milters

Now all you have to do is restarting postgres and your issue should be solved.