OpenDKIM is a milter for adding DKIM signatures to outbound mail and checking DKIM signatures on inbound mail. It can be configured to reject mail that has missing or invalid DKIM signatures.
Install the packages opendkim (the daemon that talks to the MTA and does the work) and opendkim-tools (for creating new keys and lots of other things).
apt-get install opendkim opendkim-tools
The standard directory for DKIM keys on Stretch is /etc/dkimkeys, on Jessie make that directory.
To create a key use a command such as "opendkim-genkey -D /etc/dkimkeys -d etbe.example.com -s 2017". The "selector" is to allow removing old keys for a domain and creating new ones, a common practice is to use the creation year as the selector. It also allows having multiple keys for a domain, if you have multiple servers sending mail on behalf of a domain then you can have a key for each server. Note that you can have an unlimited number of domains with the same key.
On Stretch the files in /etc/dkimkeys must be owned by opendkim.
opendkim-genkey -D /etc/dkimkeys -s $SELECTOR chown opendkim /etc/dkimkeys/*
Edit /etc/opendkim.conf and use the following settings (modified for the domain name and selector you choose). Note that if you need keys for multiple domains then you must do things differently, see a later section.
UMask 007 Domain etbe.example.com KeyFile /etc/dkimkeys/2017.private Selector 2017
Important OpenDKIM Options
The "Canonicalization" configuration option changes the signing options which can allow messages to be verified after being munged a bit by list servers etc. The "relaxed/simple" option specifies that headers can be munged a bit without breaking a message validation. The "relaxed" checks on headers MIGHT allow messages that have been through a Mailman list server to be validated, it's almost certain that they won't be validated if relaxed isn't used.
The default is to accept mail with bad signatures. To use DKIM seriously you need to reject them with the "On-?BadSignature" option.
The "BodyLengthDB" specifies a dataset that controls which messages are signed with the l= option to specify the length. When l= is used a hostile party could append data to the end of the message without breaking a signature, but it also means that a list server can add a footer to the message without breaking it. Unless you are running a list server (which should not be sending to other list servers) or other automated system you generally want l= on all mail. If the bodylengthdb.cfg has the contents ".*" it will cause every message to have the l= option.
Canonicalization relaxed/simple On-BadSignature reject BodyLengthDB refile:/etc/mail/bodylengthdb.cfg
The file /etc/dkimkeys/2017.txt needs to be included in the zone data file. You could use an include statement like the following in the file, but I usually copy/paste the contents of /etc/dkimkeys/2017.txt in to the zone file.
If you are going to use a Unix domain socket for the MTA to talk to OpenDKIM then use vipw to edit the passwd file and give the opendkim user the primary group of the mail server (EG the group postfix if Postfix is your MTA).
If you want to run your MTA chrooted (like the default configuration of Postfix) put something like the following in /etc/default/opendkim
RUNDIR=/var/spool/postfix/run/opendkim SOCKET=local:$RUNDIR/opendkim.sock PIDFILE=$RUNDIR/$NAME.pid GROUP=postfix
Add the following lines to /etc/postfix/main.cf:
smtpd_milters = unix:/run/opendkim/opendkim.sock non_smtpd_milters = unix:/run/opendkim/opendkim.sock
If you already had milters defined then append the opendkim entries after the previous one with a comma separating them. the smtpd_milters line is for SMTP connections (TCP), the non_smtpd_milters line is for local submission (IE running "mail user" on the command-line). For signing outbound mail you need both, for verifying inbound mail you only need the smtpd_milters line.
Please add this.
Mailman tends to break signatures. It doesn't pass headers through, it parses them into an internal format and regenerates them so is almost guaranteed to break messages that don't have signatures with "relaxed" or "relaxed/simple" canonicalisation. It can also rewrite the body into a different MIME encoding.
Edit "/etc/mailman/mm_cfg.py" to have the directive "REMOVE_DKIM_HEADERS = Yes". The OpenDKIM instance on the list server should reject mail that is not signed correctly so there is no need for the headers to be forwarded on. Removing the DKIM headers removes the possibility of broken signatures. You must restart Mailman after doing this.
Configure the list server to use DKIM on outbound mail. As a general rule list servers don't send mail to other list servers so there aren't going to be many problems with this.
For messages that use DMARC (which uses DNS to tell the recipient that they should be signed) you need to rewrite the header to not have the original sender address. You can set the "dmarc_moderation_action" (in the web based configuration for each list) to "Munge From" to do this. But this doesn't seem to apply to ADSP (the other standard method of using DNS to tell the recipient the signing policy) and also won't apply to custom settings (EG a MTA specially configured to insist on signatures from it's own domains).
To properly handle mail from domains using ADSP or other non-DMARC ways of specifying that mail must be signed the solution is to set "from_is_list" (in the web based configuration for each list) to "Rewrite". That will munge the From field on all mail to say that it's from the list.
The command opendkim-testkey will read the configuration of the opendkim daemon and do DNS checks to determine if the configuration is likely to work. It can take multiple "-v" options for verbosity. In the below example "key not secure" means that DNSSEC is not in use (someone should add a DNSSEC section to the Bind9 page).
# opendkim-testkey -v -v opendkim-testkey: using default configfile /etc/opendkim.conf opendkim-testkey: key loaded from /etc/dkimkeys/2017.private opendkim-testkey: checking key '2017._domainkey.etbe.example.com' opendkim-testkey: key not secure
One issue with opendkim-testkey is that it will do all it's checks against external DNS servers, so it can't do anything useful on a test environment like an example.com domain unless you redirect all it's queries to a recursive DNS server you control. Here are example iptables commands to do that:
iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to $RECURSIVE:53 iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to $RECURSIVE:53
The command opendkim-testmsg will sign or verify a message. To verify a message just redirect stdin to the message and it will say something on stderr if it thinks there's a problem. If there are no problems it will be silent.