Differences between revisions 32 and 80 (spanning 48 versions)
Revision 32 as of 2020-01-06 10:11:17
Size: 12122
Editor: ?DavidBürgin
Comment: Add note /etc/default/opendkim
Revision 80 as of 2022-04-01 18:07:14
Size: 24326
Editor: ?DavidBürgin
Comment: Clarify use of absolute path in unix: socket
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:
The Debian '''opendkim package''' is a full-featured DKIM implementation
suitable for use with MTAs (mail servers) such as [[Postfix]].

!DomainKeys Identified Mail (DKIM) combines several existing antiphishing and antispam methods to improve the quality of the classification and identification of legitimate e-mail. Instead of the traditional IP-address, to determine the message sender DKIM adds a digital signature associated with the domain name of the organization.

The '''opendkim''' package contains a full-featured DKIM milter ([[https://en.wikipedia.org/wiki/Milter| mail filter]]) implementation
suitable for use with MTAs (Message Transfer Agent mail servers) such as [[Postfix]]. Upstream development at [[http://www.opendkim.org/| OpenDKIM]] is a community effort.

!DomainKeys Identified Mail (DKIM) combines several existing antiphishing and antispam methods to improve the quality of the classification and identification of legitimate e-mail. Instead of the traditional IP-address to determine the message sender, DKIM adds a digital signature associated with the domain name of the organization.  In tandem, DNS is used to publish TXT records with the public portion of the cryptographic certificate used for digital signing.

OpenDKIM can add DKIM signatures to outbound mail and check DKIM signatures on inbound mail. It can be configured to reject mail that has missing or invalid DKIM signatures.
Line 12: Line 16:
{{{#!wiki caution
Page is being revised.

(January 2020)
}}}
Line 22: Line 20:
This section has everything to set up a fully functional, minimal installation
of opendkim for signing and verifying integrated with [[Postfix]].

This is the quick, five-minute version of opendkim setup suitable only for a
single domain. For a fuller discussion of the different setup options available
please refer to the subsequent sections.

Let’s go! First, install opendkim.
The quickstart instructions in this section describe setting up a minimal, but
functional installation of opendkim for signing and verifying, integrated with
[[Postfix]]. This is the five-minute version of opendkim configuration for the
impatient. For a fuller discussion of the setup options available, please refer
to the subsequent sections.

Let’s go! First, install opendkim:
Line 35: Line 32:
Next, generate the private key for your domain and selector:

{{{
sudo opendkim-genkey -D /etc/dkimkeys -d yourdomain.com -s 2020 -S
}}}

Now, edit /etc/opendkim.conf. At a minimum, four parameters need to be adapted:
the
domain/selector/key file triple, and the socket. For the socket, the easiest
option is to use a TCP socket listening on a local port.

{{{
Domain yourdomain.com
Selector 2020
KeyFile /etc/dkimkeys/2020
.private
Next, generate the key pair for your DNS domain and ''selector'':

{{{
sudo -u opendkim opendkim-genkey -D /etc/dkimkeys -d yourdomain.org -s 2021
}}}

Now, edit /etc/opendkim.conf. Four parameters need to be adapted: the
domain/selector/key file triple, and the socket. For the socket, the easiest
option is to use a TCP socket listening on a local port (bypassing socket file ownership or chroot access issues).

{{{
Domain yourdomain.org
Selector 2021
KeyFile /etc/dkimkeys/2
021.private
Line 56: Line 53:
The final step is integrating the new opendkim service with Postfix. Edit The final step is integrating the opendkim service with Postfix. Edit
Line 67: Line 64:
That’s it! Your mail is now being signed and verified. Don’t forget to publish
your public key as a TXT record in DNS at {{{2020._domainkey.yourdomain.com}}}.
The generated file /etc/dkimkeys/2020.txt contains that record for your
convenience.
Done! Your mail is now being signed and verified. Don’t forget to publish your
public key as a TXT record in DNS at {{{2021._domainkey.yourdomain.org}}}. The
generated file /etc/dkimkeys/2021.txt contains that record for your convenience.
Line 75: Line 71:

These sections assume that you have installed the opendkim and opendkim-tools
packages.

{{{#!wiki caution
Under revision.
}}}
See the manual page [[https://manpages.debian.org/man/opendkim.conf.5|opendkim.conf(5)]]
for reference.
Line 84: Line 75:
The opendkim '''configuration file''' can be found at /etc/opendkim.conf. All
configuration parameters should be set in this file.
The opendkim '''configuration file''' can be found at `/etc/opendkim.conf` . As of Debian 9 "stretch" all configuration parameters should be set in this file.
Line 88: Line 78:
information on this online. Previously, one would edit the default settings at
'''/etc/default/opendkim''', and then execute
information on this online.  Previously ([[https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=864162|#864162]]), one would edit the default settings at
''/etc/default/opendkim'', and then execute
Line 96: Line 86:

== Installation details ==
The opendkim and opendkim-tools packages should already be installed. See [[#Quickstart]] for basic steps.

The standard directory for DKIM keys on Debian 9 "stretch" and later releases, is /etc/dkimkeys.<<BR>>
On Debian 8 "jessie" that directory must be created. This directory contains private encryption keys, and should thus be owned and set to only be accessible by user ''opendkim''.
{{{
# only for Debian 8 "jessie"
mkdir /etc/dkimkeys/
chown opendkim:opendkim /etc/dkimkeys
chmod 0700 /etc/dkimkeys
}}}



Line 99: Line 105:
{{{opendkim-genkey}}} program.

{{{
sudo opendkim-genkey \
'''opendkim-genkey''' program. This program generates a private key named
{{{<selector>.private}}} in the specified directory, as well as a public key {{{<selector>.txt}}} ready to be included in a bind DNS zone file. An example invocation:

{{{
sudo --user opendkim opendkim-genkey \
Line 104: Line 111:
    --domain=yourdomain.com \
    --selector=2020 \
    --domain=yourdomain.org \
    --selector=2021 \
Line 109: Line 116:
You will want to tweak some of these options. For example, you might not want to
forbid subdomain signing with {{{--nosubdomains}}}, or you might want to
restrict usage to email with {{{--restrict}}}. The cryptography has reasonably
strong default settings, so it is usually not necessary to specify {{{--bits}}}
(default: 2048) and {{{--hash-algorithms}}} (default: SHA-256).

The directory /etc/dkimkeys is created by the opendkim package as the
Debian-specific canonical key storage location. It is owned by user opendkim.
You could give it to root instead, but see the next section for a discussion of
key ownership and permissions.

Notice how we execute {{{opendkim-genkey}}} as root. That way,
{{{opendkim-genkey}}} produces key files with the correct, restricted
permissions, owned by root.

In the next section, an alternative scheme is described, where opendkim runs as
unprivileged user opendkim even when it reads the key file. In that case, it is
important that the key file be owned by user opendkim. So, if you plan to use
that scheme, be sure to execute {{{opendkim-genkey}}} as user opendkim instead:

{{{
sudo -u opendkim opendkim-genkey ...
}}}
You will want to tweak some of these options. For example, you might not want
to forbid subdomain signing with {{{--nosubdomains}}}, or you might want to
restrict usage to email with {{{--restrict}}}. In Debian, the cryptographic
options use reasonably strong defaults, so it is usually not necessary to
specify {{{--bits}}} (default: 2048) and {{{--hash-algorithms}}} (default:
SHA-256).

The ''selector'' allows for removing old keys from a domain and creating new ones. One common practice is to use the creation year as the selector. <<BR>>
It's also possible to have multiple keys for a domain, if there are multiple servers sending mail on behalf of a domain then each can have a unique key. Note that one can also have an unlimited number of domains with the same key.

In Debian, the directory '''/etc/dkimkeys''' serves as the canonical key storage
location, and is created by the opendkim package on installation. The owner is
set to opendkim by default, and access is restricted to the user, so private keys remain safe.

Notice how we execute opendkim-genkey as user opendkim. That way,
opendkim-genkey produces key files with the correct, restricted permissions,
owned by opendkim.

It is also possible to restrict key ownership further to user root by invoking
opendkim-genkey directly as root:

{{{
sudo opendkim-genkey ...
}}}

However, while this does work, opendkim will not be able to ''reload'' keys
during operation: during startup, the key file may be read into memory as root,
but subsequently, after root privileges are dropped, the keys will be accessed
as user opendkim. This is discussed in the following section.
Line 143: Line 156:
This setting instructs opendkim to ''become'' user opendkim.

So, more accurately, the opendkim service starts life as root, does everything
it needs to do as root – such as reading in keys –, and then, before beginning
normal operation, it drops the root privileges and becomes user opendkim. This
is a standard, secure procedure that should be appropriate for most users.
This setting instructs opendkim to ''become'' user opendkim. So, more
accurately, what happens is that the opendkim service is started ''as root'', does
everything it needs to do as root – such as reading private keys and writing the
pid file –, and then, before beginning normal operation, it drops the root
privileges and becomes user opendkim. This is a standard, secure procedure that
should be appropriate for most users.
Line 157: Line 170:
opendkim service as opendkim from the very beginning, with no root privileges
involved at any stage. Please note that most users will not need this.
opendkim service as user opendkim from the very beginning, with no root
privileges involved at any stage. Please note that most users do not need this.
Line 170: Line 183:
The default {{{UserID opendkim}}} setting in /etc/opendkim.conf can now be
removed, as no privilege dropping is necessary.

Reload the systemd configuration with {{{sudo systemctl daemon-reload}}}, and
restart the opendkim service. It now runs as an unprivileged user.

Ensure that the unprivileged user can actually read the keys in /etc/dkimkeys,
see the discussion in the preceding section
.
The {{{UserID opendkim}}} setting in /etc/opendkim.conf can now be removed, as
no privilege dropping is necessary. Reload the systemd configuration with
{{{sudo systemctl daemon-reload}}}, and restart the opendkim service. It now
runs as an unprivileged user.

Make sure that the unprivileged user can actually read the keys in
/etc/dkimkeys (see above)
, and write the pid file. (Since both /etc/dkimkeys and the runtime
directory /run/opendkim come owned by user/group opendkim, this should work
without further adjustment.)
Line 181: Line 195:
TODO Discuss KeyTable SigningTable

== Socket ==

TODO

=== Using a local UNIX socket ===

== DNS resolution ==

TODO

== Postfix and opendkim ==

Install the package:

{{{
apt-get install opendkim opendkim-tools
}}}

Add to the Postfix signature opendkim. In Debian stretch, the recommended place for the private key is /etc/dkimkeys (it already exists with owner opendkim, group opendkim, and rwx permissions for user only).

Generate a key for mail.example.com server {{{
  opendkim-genkey -D /etc/dkimkeys/ -d example.com -s mail
}}}

resulting in the directory /etc/dkimkeys/ 2 files : mail.private and mail.txt (private and public key, respectively). Ensure that only the opendkim user can read them as that is enough: {{{
chgrp opendkim /etc/dkimkeys/*
chmod go-rwx /etc/dkimkeys/*
}}}

Postfix and opendkim will communicate through a unix socket (faster and more secure than a tcp socket). The default configuration of postfix in debian runs under a chroot (/var/spool/postfix) so the socket must be created there : {{{
sockdir=/var/spool/postfix/var/run/opendkim
mkdir -p $sockdir
chown opendkim. $sockdir
chmod go-rwx $sockdir
chmod g+x $sockdir
}}}
For a single-domain DKIM setup with only a single key, the configuration shown
in [[#Quickstart]], using the three parameters {{{Domain}}}, {{{Selector}}},
{{{KeyFile}}} is enough. However, opendkim configuration supports multiple
domains and keys, read from a variety of sources (files, SQL databases, Lua
scripts, …). '''{{{KeyTable}}}''' and '''{{{SigningTable}}}''' are the
configuration parameters that enable this. For mail servers that are "smarthosts", opendkim can be configured to sign messages from subnets of trusted systems via the '''{{{InternalHosts}}}''' parameter.
Line 222: Line 204:
All the available options can be found on the page: http://www.opendkim.org/opendkim.conf.5.html {{{
Syslog yes

# Required to use local socket with MTAs that access the socket as a non-
# privileged user (e.g. Postfix)
UMask 007

# Signature mode and signature verification
Mode sv # not needed as this is the default

# DO NOT BELIEVE that /etc/default/opendkim overrides the following :
# (In stretch, it does not on 2019-08-13)
Socket local:/var/spool/postfix/var/run/opendkim/opendkim.sock
{{{
Line 245: Line 214:
mail._domainkey.example.com example.com:mail:/etc/dkimkeys/mail.private mail._domainkey.yourdomain.org yourdomain.org:mail:/etc/dkimkeys/mail.private
Line 250: Line 219:
# Domain example.com
*@example.com mail._domainkey.example.com
# Domain yourdomain.org
*@yourdomain.org mail._domainkey.yourdomain.org
Line 262: Line 231:
You do not need to modify /etc/default/opendkim.

Now restart opendkim and check that everything is ok: {{{

# service opendkim restart
# service opendkim status
# ls -ld /var/run/opendkim
drwxr-x--- 2 opendkim opendkim 60 Aug 13 13:57 /var/run/opendkim
# ls -l /var/run/opendkim
total 4
-rw-rw---- 1 root root 6 Aug 13 13:57 opendkim.pid
# ls -ld /var/spool/postfix/var/run/opendkim
drwx--x--- 2 opendkim opendkim 4096 Aug 13 13:57 /var/spool/postfix/var/run/opendkim
# ls -l /var/spool/postfix/var/run/opendkim
total 0
srwxrwx--- 1 opendkim opendkim 0 Aug 13 13:57 opendkim.sock
root@serveur:~/config#
}}}

Now, add postfix to the group opendkim so that it can communicate with opendkim: {{{
adduser postfix opendkim
}}}

And add the following lines to /etc/postfix/main.cf: {{{
  milter_default_action = accept
  milter_protocol = 6
  # from inside the chroot, the socket will be in /var/run/opendkim
  smtpd_milters = unix:/var/run/opendkim/opendkim.sock
  non_smtpd_milters = unix:/var/run/opendkim/opendkim.sock
}}}

The setup of opendkim and postfix is complete.

Restart postfix

{{{
service postfix restart
service postfix status
}}}


== Important OpenDKIM options ==

The '''{{{Canonicalization}}}''' configuration parameter 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. If you want to reject such mail, use the '''{{{On-BadSignature}}}''' parameter.

The '''{{{BodyLengthDB}}}''' parameter 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
}}}


== Socket ==

The opendkim service has to provide a communication channel for the MTA
(Postfix). A TCP socket listening on a port only accessible locally is a
reasonable choice that is also easy to set up.

{{{
Socket inet:8891@localhost
}}}

Sockets can be of IPv4 or IPv6 type, and can listen on all interfaces or on a
specific interface only.

Some prefer setting up a UNIX domain socket instead, as a faster and more secure
channel (though opinion on this point varies). This requires a little more
configuration work, and is described in the following section.

=== Using a UNIX domain socket ===

The UNIX domain socket file must be accessible to the MTA. In Debian, Postfix
runs in a chroot jail in /var/spool/postfix by default, so the socket must be
below that path.

Postfix does not prescribe a standard location for UNIX sockets in its chroot.
You can mimic the /run directory hierarchy, and place the socket below
/var/spool/postfix/run/opendkim, or you can simply claim a top-level directory
like /var/spool/postfix/opendkim. Here we go with the latter.

First, create the directory, owned by opendkim and world-inaccessible:

{{{
sudo mkdir -m o-rwx /var/spool/postfix/opendkim
sudo chown opendkim: /var/spool/postfix/opendkim
}}}

Then, configure the socket in /etc/opendkim.conf:

{{{
Socket local:/var/spool/postfix/opendkim/opendkim.sock
}}}

Next, add user postfix to group opendkim. Postfix then relies on the group
permissions to actually access the socket:

{{{
sudo adduser postfix opendkim
}}}

Finally, adjust the Postfix configuration in /etc/postfix/main.cf to use the
desired socket path:

{{{
smtpd_milters = unix:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
}}}

Note that when Postfix runs chrooted (the default in Debian), an absolute pathname
here is interpreted relative to the Postfix queue directory /var/spool/postfix.

Don’t forget to restart opendkim and postfix to apply the settings.

== DNS resolution ==

In Debian, opendkim is compiled with '''libunbound''', a DNSSEC-capable
asynchronous resolver library. It is important to be aware of this, because it
means opendkim does DNS queries for DKIM keys independently, that is, it does
not go through any local resolver and does not take into account configuration
at /etc/resolv.conf.

The default opendkim configuration ships with a valid trust anchor setting,
{{{TrustAnchorFile /usr/share/dns/root.key}}}, thus letting opendkim do DNSSEC
queries out-of-the-box.

Advanced users should be aware of two additional configuration parameters.

The '''{{{Nameservers}}}''' parameter can be used to override the name servers
that libunbound uses. For example, you may already have an Unbound resolver
running locally (a relatively typical setup in a mail server). In that case, a
setting like the following instructs opendkim to send DNS queries through that
resolver:

{{{
Nameservers 127.0.0.1
}}}

The '''{{{ResolverConfiguration}}}''' parameter can be used to pass an Unbound
configuration file
([[https://nlnetlabs.nl/documentation/unbound/unbound.conf/|unbound.conf(5)]])
to libunbound. Using this, more sophisticated customization regarding DNS
resolution in opendkim is possible.

{{{
ResolverConfiguration /etc/opendkim/unbound.conf
}}}

{{{{#!wiki warning
The Debian unbound package installs a default configuration file at
'''/etc/unbound/unbound.conf'''. Do not attempt to use this file unchanged with
{{{ResolverConfiguration}}}! opendkim will just quietly shut down.

The reason for the incompatibility is that the shipped unbound.conf includes an
{{{auto-trust-anchor-file}}} setting, for which opendkim does not have the
necessary permissions. Unfortunately, libunbound is rather fragile in this area.
Prepare your own unbound.conf for opendkim and test carefully.
}}}}

== Postfix integration ==

The opendkim service functions as a ''milter'', that is, a plugin software
hooked into the SMTP processing of the Postfix MTA. To enable a milter, it is
enough to tell Postfix on which socket the milter application is listening.
Example /etc/postfix/main.cf:

{{{
smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters
}}}

With opendkim, two additional milter configuration parameters in
/etc/postfix/main.cf are useful.

The '''{{{milter_default_action}}}''' parameter determines what to do when a
milter fails, for example, when it does not respond after a crash. In order to
avoid losing mail, it is best to set this to {{{accept}}}:

{{{
milter_default_action = accept
}}}

Postfix does not pass internally-generated messages such as bounce messages to
opendkim, so by default bounces are not DKIM-signed. This can be a problem if
you also use a strict DMARC policy, because it may cause your unsigned bounce
messages themselves to get rejected. The
'''{{{internal_mail_filter_classes}}}''' parameter can be used to pass bounces
through the milters as well:

{{{
internal_mail_filter_classes = bounce
}}}

Further details of milter usage in Postfix can be found in its
[[http://www.postfix.org/MILTER_README.html|MILTER_README]].
Line 305: Line 394:
Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to bind to port local:/var/spool/postfix/var/run/opendkim/opendkim.sock: No such file or directory
Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to create listening socket on conn local:/var/spool/postfix/var/run/opendkim/opendkim.sock
Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to bind to port local:/var/spool/postfix/opendkim/opendkim.sock: No such file or directory
Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to create listening socket on conn local:/var/spool/postfix/opendkim/opendkim.sock
Line 311: Line 400:
Aug 13 13:46:19 yourhostname postfix/cleanup[17588]: warning: connect to Milter service unix:/var/run/opendkim/opendkim.sock: No such file or directory
}}}
then that means postfix could not read the socket. Did you put postfix in group opendkim? Are the permissions on /var/spool/postfix/var/run/opendkim/opendkim.sock correct?
Aug 13 13:46:19 yourhostname postfix/cleanup[17588]: warning: connect to Milter service unix:/opendkim/opendkim.sock: No such file or directory
}}}
then that means postfix could not read the socket. Did you put postfix in group opendkim? Are the permissions on /var/spool/postfix/opendkim/opendkim.sock correct?
Line 319: Line 408:
Add a TXT record for your example.com domain Add a `_domainkey` TXT record for yourdomain.org and ''selector'' (e.g. 2021).
Line 322: Line 411:
|| mail._domainkey || TXT || v=DKIM1; k=rsa; p=MI.. (take it from /etc/dkimkeys/mail.txt file; remove the >"< and connect the lines after p= to one key.) || || 2021._domainkey || TXT || v=DKIM1; k=rsa; p=MI.. (take it from /etc/dkimkeys/2021.txt file; remove the >"< and connect the lines after p= to one key.) ||


BIND Configuration : The public key file /etc/dkimkeys/2021.txt created by `opendkim-genkey` is in a format ready to be included in a DNS bind9 zone data file. It can be copied to a bind9 server, and pasted into a zone file, or the [[https://bind9.readthedocs.io/en/latest/reference.html#the-include-directive|$INCLUDE]] statement may be used.
  {{{
$INCLUDE /etc/dkimkeys/2021.txt
}}}



Line 325: Line 424:

You can test your installation with opendkim-testkey: {{{
# opendkim-testkey -d example.com -s mail -vvv
There are several checks one can perform to validate an OpenDKIM installation.

== opendkim-testkey ==

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. A specific domain to check can be specified with optional "-d" option.

{{{
# opendkim-testkey -v -v
Line 330: Line 433:
opendkim-testkey: checking key 'mail._domainkey.example.com' opendkim-testkey: key loaded from /etc/dkimkeys/2021.private
opendkim-testkey: checking key '2021._domainkey.etbe.yourdomain.org'
Line 332: Line 436:
opendkim-testkey: key OK
}}}
}}}

In the example above "key not secure" means that DNSSEC is not in use (someone should add a DNSSEC section to the [[Bind9]] page).


One issue with opendkim-testkey is that by default it (and the opendkim daemon) will do all its checks against external DNS servers, so it can't do anything useful on a test environment like an example.org 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
}}}

== opendkim-testmsg ==

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.


== email tests ==

In addition to the local testing commands listed above, a public DKIM configuration can be tested by sending an empty email to `check-auth@verifier.port25.com` . An automated reply should be received within a few minutes.

Another option may be https://www.mail-tester.com/ . Visit the website, email the test address, and click the website link to get feedback on junk email and DKIM ratings.


= Mailman and DKIM Configuration =

Mailman tends to break DKIM 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" [[opendkim#Important_OpenDKIM_options|canonicalization]]. 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.

See the [[https://wiki.list.org/DEV/DMARC|Mailman DMARC Wiki page]] for a summary of the DMARC options and the [[https://wiki.list.org/DEV/DKIM|Mailman DKIM Wiki page]] for a lot of background information.




= FAQ =

 Q. OpenDKIM fails to start after upgrading from Debian 8 "jessie" to stretch :: A) If OpenDKIM fails to start after upgrading from Debian 8 "jessie" to stretch it seems that you have to auto-generate the Systemd service file for OpenDKIM.

  Run these commands:
 {{{
/lib/opendkim/opendkim.service.generate
systemctl daemon-reload
service opendkim restart
}}}

  https://serverfault.com/a/847442/225489 - credit

Line 336: Line 493:

 * http://www.opendkim.org/ : Official website
  https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy - blog tutorial on DKIM

  https://words.bombast.net/postfix-with-spf-dkim-and-dmarc/ - blog tutorial on DKIM, SPF, and DMARC with Debian Jessie (circa 2016 but still applicable)

  https://www.linuxbabe.com/mail-server/setting-up-dkim-and-spf - blog tutorial on DKIM and SPF on Ubuntu (circa 2021)
 
  http://www.dkim.org/info/DKIM-Intro-Allman.pdf - overview of DKIM by Eric Allman from Sendmail circa 2005

  https://support.google.com/mail/troubleshooter/2696779?hl=en#ts=9283752%2C2696841%2C2696793%2C9288748 - google email delivery troubleshooting, including DKIM, SPF, and DMARC

  http://www.postfix.org/MILTER_README.html - Postfix milter info

  


== upstream specific information ==
  http://www.opendkim.org/ - Homepage

  https://github.com/trusteddomainproject/OpenDKIM - Bug/Feature Tracking

  http://www.opendkim.org/docs.html - Documentation


== Debian-specific information ==
  [[DebianPkg:opendkim|opendkim]] packages

  [[DebianBug:opendkim|Bugs]]

  [[DebianMan:opendkim|Manpages]]
   
     [[https://manpages.debian.org/man/opendkim.conf.5|opendkim.conf(5)]]: Configuration parameters manual page

     [[https://manpages.debian.org/man/opendkim-lua.3|opendkim-lua(3)]]: Lua scripting interface manual page

  [[https://security-tracker.debian.org/tracker/source-package/opendkim|Security Bug Tracker]]


Line 340: Line 533:
CategoryNetwork CategoryNetwork CategoryMail CategorySoftware

Translation(s): English - Русский


The opendkim package contains a full-featured DKIM milter (mail filter) implementation suitable for use with MTAs (Message Transfer Agent mail servers) such as Postfix. Upstream development at OpenDKIM is a community effort.

DomainKeys Identified Mail (DKIM) combines several existing antiphishing and antispam methods to improve the quality of the classification and identification of legitimate e-mail. Instead of the traditional IP-address to determine the message sender, DKIM adds a digital signature associated with the domain name of the organization. In tandem, DNS is used to publish TXT records with the public portion of the cryptographic certificate used for digital signing.

OpenDKIM can add DKIM signatures to outbound mail and check DKIM signatures on inbound mail. It can be configured to reject mail that has missing or invalid DKIM signatures.

Jump to #Quickstart for an overview and minimal setup, and refer to #Configuration for the complete picture.

Quickstart

The quickstart instructions in this section describe setting up a minimal, but functional installation of opendkim for signing and verifying, integrated with Postfix. This is the five-minute version of opendkim configuration for the impatient. For a fuller discussion of the setup options available, please refer to the subsequent sections.

Let’s go! First, install opendkim:

sudo apt install opendkim opendkim-tools

Next, generate the key pair for your DNS domain and selector:

sudo -u opendkim opendkim-genkey -D /etc/dkimkeys -d yourdomain.org -s 2021

Now, edit /etc/opendkim.conf. Four parameters need to be adapted: the domain/selector/key file triple, and the socket. For the socket, the easiest option is to use a TCP socket listening on a local port (bypassing socket file ownership or chroot access issues).

Domain   yourdomain.org
Selector 2021
KeyFile  /etc/dkimkeys/2021.private

Socket   inet:8891@localhost

That’s it for opendkim. Restart the service with sudo systemctl restart opendkim.

The final step is integrating the opendkim service with Postfix. Edit /etc/postfix/main.cf to connect the two:

smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters

And finally reload the Postfix configuration with sudo systemctl reload postfix.

Done! Your mail is now being signed and verified. Don’t forget to publish your public key as a TXT record in DNS at 2021._domainkey.yourdomain.org. The generated file /etc/dkimkeys/2021.txt contains that record for your convenience.

Configuration

The following sections discuss opendkim configuration options in more detail. See the manual page opendkim.conf(5) for reference.

The opendkim configuration file can be found at /etc/opendkim.conf . As of Debian 9 "stretch" all configuration parameters should be set in this file.

This needs to be stated, because there is a lot of older, now misleading information on this online. Previously (#864162), one would edit the default settings at /etc/default/opendkim, 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.

Installation details

The opendkim and opendkim-tools packages should already be installed. See #Quickstart for basic steps.

The standard directory for DKIM keys on Debian 9 "stretch" and later releases, is /etc/dkimkeys.
On Debian 8 "jessie" that directory must be created. This directory contains private encryption keys, and should thus be owned and set to only be accessible by user opendkim.

# only for Debian 8 "jessie"
mkdir /etc/dkimkeys/
chown opendkim:opendkim /etc/dkimkeys
chmod 0700 /etc/dkimkeys

Generating keys

For key generation, the opendkim-tools package provides the opendkim-genkey program. This program generates a private key named <selector>.private in the specified directory, as well as a public key <selector>.txt ready to be included in a bind DNS zone file. An example invocation:

sudo --user opendkim opendkim-genkey \
    --directory=/etc/dkimkeys \
    --domain=yourdomain.org \
    --selector=2021 \
    --nosubdomains

You will want to tweak some of these options. For example, you might not want to forbid subdomain signing with --nosubdomains, or you might want to restrict usage to email with --restrict. In Debian, the cryptographic options use reasonably strong defaults, so it is usually not necessary to specify --bits (default: 2048) and --hash-algorithms (default: SHA-256).

The selector allows for removing old keys from a domain and creating new ones. One common practice is to use the creation year as the selector.
It's also possible to have multiple keys for a domain, if there are multiple servers sending mail on behalf of a domain then each can have a unique key. Note that one can also have an unlimited number of domains with the same key.

In Debian, the directory /etc/dkimkeys serves as the canonical key storage location, and is created by the opendkim package on installation. The owner is set to opendkim by default, and access is restricted to the user, so private keys remain safe.

Notice how we execute opendkim-genkey as user opendkim. That way, opendkim-genkey produces key files with the correct, restricted permissions, owned by opendkim.

It is also possible to restrict key ownership further to user root by invoking opendkim-genkey directly as root:

sudo opendkim-genkey ...

However, while this does work, opendkim will not be able to reload keys during operation: during startup, the key file may be read into memory as root, but subsequently, after root privileges are dropped, the keys will be accessed as user opendkim. This is discussed in the following section.

User and privileges

By default, the opendkim service runs as user opendkim. This is because the default configuration contains a setting for parameter UserID in /etc/opendkim.conf:

UserID opendkim

This setting instructs opendkim to become user opendkim. So, more accurately, what happens is that the opendkim service is started as root, does everything it needs to do as root – such as reading private keys and writing the pid file –, and then, before beginning normal operation, it drops the root privileges and becomes user opendkim. This is a standard, secure procedure that should be appropriate for most users.

An alternative setup is possible where the opendkim service runs as an unprivileged user from the very start, and this is described in the following section.

Running as an unprivileged user

For setups that have additional security requirements, it is possible to run the opendkim service as user opendkim from the very beginning, with no root privileges involved at any stage. Please note that most users do not need this.

Create a systemd override file at /etc/systemd/system/opendkim.service.d/override.conf (you may need to create the directory too), with the following contents:

[Service]
User=opendkim
Group=opendkim

The UserID opendkim setting in /etc/opendkim.conf can now be removed, as no privilege dropping is necessary. Reload the systemd configuration with sudo systemctl daemon-reload, and restart the opendkim service. It now runs as an unprivileged user.

Make sure that the unprivileged user can actually read the keys in /etc/dkimkeys (see above), and write the pid file. (Since both /etc/dkimkeys and the runtime directory /run/opendkim come owned by user/group opendkim, this should work without further adjustment.)

Key selection

For a single-domain DKIM setup with only a single key, the configuration shown in #Quickstart, using the three parameters Domain, Selector, KeyFile is enough. However, opendkim configuration supports multiple domains and keys, read from a variety of sources (files, SQL databases, Lua scripts, …). KeyTable and SigningTable are the configuration parameters that enable this. For mail servers that are "smarthosts", opendkim can be configured to sign messages from subnets of trusted systems via the InternalHosts parameter.

Setup the /etc/opendkim.conf:

# Specify the list of keys
KeyTable file:/etc/dkimkeys/keytable
# Match keys and domains. To use regular expressions in the file, use refile: instead of file:
SigningTable refile:/etc/dkimkeys/signingtable 
# Match a list of hosts whose messages will be signed. By default, only localhost is considered as internal host.
InternalHosts refile:/etc/dkimkeys/trustedhosts

Now in the file /etc/dkimkeys/keytable, put information about the private key:

mail._domainkey.yourdomain.org yourdomain.org:mail:/etc/dkimkeys/mail.private 

In the file /etc/dkimkeys/signingtable, specify which key will sign a domain:

# Domain yourdomain.org
*@yourdomain.org mail._domainkey.yourdomain.org
# You can specify multiple domains
# Example.net www._domainkey.example.net 

In the file /etc/dkimkeys/trustedhosts, specify which hosts will have messages signed. If needed, include localhost as it is not implicit:

127.0.0.1
10.1.0.0/16
1.2.3.4/24

Important OpenDKIM options

The Canonicalization configuration parameter 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. If you want to reject such mail, use the On-BadSignature parameter.

The BodyLengthDB parameter 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

Socket

The opendkim service has to provide a communication channel for the MTA (Postfix). A TCP socket listening on a port only accessible locally is a reasonable choice that is also easy to set up.

Socket inet:8891@localhost

Sockets can be of IPv4 or IPv6 type, and can listen on all interfaces or on a specific interface only.

Some prefer setting up a UNIX domain socket instead, as a faster and more secure channel (though opinion on this point varies). This requires a little more configuration work, and is described in the following section.

Using a UNIX domain socket

The UNIX domain socket file must be accessible to the MTA. In Debian, Postfix runs in a chroot jail in /var/spool/postfix by default, so the socket must be below that path.

Postfix does not prescribe a standard location for UNIX sockets in its chroot. You can mimic the /run directory hierarchy, and place the socket below /var/spool/postfix/run/opendkim, or you can simply claim a top-level directory like /var/spool/postfix/opendkim. Here we go with the latter.

First, create the directory, owned by opendkim and world-inaccessible:

sudo mkdir -m o-rwx /var/spool/postfix/opendkim
sudo chown opendkim: /var/spool/postfix/opendkim

Then, configure the socket in /etc/opendkim.conf:

Socket local:/var/spool/postfix/opendkim/opendkim.sock

Next, add user postfix to group opendkim. Postfix then relies on the group permissions to actually access the socket:

sudo adduser postfix opendkim

Finally, adjust the Postfix configuration in /etc/postfix/main.cf to use the desired socket path:

smtpd_milters = unix:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

Note that when Postfix runs chrooted (the default in Debian), an absolute pathname here is interpreted relative to the Postfix queue directory /var/spool/postfix.

Don’t forget to restart opendkim and postfix to apply the settings.

DNS resolution

In Debian, opendkim is compiled with libunbound, a DNSSEC-capable asynchronous resolver library. It is important to be aware of this, because it means opendkim does DNS queries for DKIM keys independently, that is, it does not go through any local resolver and does not take into account configuration at /etc/resolv.conf.

The default opendkim configuration ships with a valid trust anchor setting, TrustAnchorFile /usr/share/dns/root.key, thus letting opendkim do DNSSEC queries out-of-the-box.

Advanced users should be aware of two additional configuration parameters.

The Nameservers parameter can be used to override the name servers that libunbound uses. For example, you may already have an Unbound resolver running locally (a relatively typical setup in a mail server). In that case, a setting like the following instructs opendkim to send DNS queries through that resolver:

Nameservers 127.0.0.1

The ResolverConfiguration parameter can be used to pass an Unbound configuration file (unbound.conf(5)) to libunbound. Using this, more sophisticated customization regarding DNS resolution in opendkim is possible.

ResolverConfiguration /etc/opendkim/unbound.conf

The Debian unbound package installs a default configuration file at /etc/unbound/unbound.conf. Do not attempt to use this file unchanged with ResolverConfiguration! opendkim will just quietly shut down.

The reason for the incompatibility is that the shipped unbound.conf includes an auto-trust-anchor-file setting, for which opendkim does not have the necessary permissions. Unfortunately, libunbound is rather fragile in this area. Prepare your own unbound.conf for opendkim and test carefully.

Postfix integration

The opendkim service functions as a milter, that is, a plugin software hooked into the SMTP processing of the Postfix MTA. To enable a milter, it is enough to tell Postfix on which socket the milter application is listening. Example /etc/postfix/main.cf:

smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters

With opendkim, two additional milter configuration parameters in /etc/postfix/main.cf are useful.

The milter_default_action parameter determines what to do when a milter fails, for example, when it does not respond after a crash. In order to avoid losing mail, it is best to set this to accept:

milter_default_action = accept

Postfix does not pass internally-generated messages such as bounce messages to opendkim, so by default bounces are not DKIM-signed. This can be a problem if you also use a strict DMARC policy, because it may cause your unsigned bounce messages themselves to get rejected. The internal_mail_filter_classes parameter can be used to pass bounces through the milters as well:

internal_mail_filter_classes = bounce

Further details of milter usage in Postfix can be found in its MILTER_README.

Troubleshooting

Try to send a mail. If you see in /var/log/mail.log something like

Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to bind to port local:/var/spool/postfix/opendkim/opendkim.sock: No such file or directory
Aug 13 13:18:00 yourhostname opendkim[15765]: OpenDKIM Filter: Unable to create listening socket on conn local:/var/spool/postfix/opendkim/opendkim.sock

then that probably means that you did not create the directory for the socket (see above) or you gave it the wrong permissions. Double-check!

If you see

Aug 13 13:46:19 yourhostname postfix/cleanup[17588]: warning: connect to Milter service unix:/opendkim/opendkim.sock: No such file or directory

then that means postfix could not read the socket. Did you put postfix in group opendkim? Are the permissions on /var/spool/postfix/opendkim/opendkim.sock correct?

If everything is correct, that does not mean your configuration of DKIM is complete: you must configure the DNS.

DNS Configuration

Add a _domainkey TXT record for yourdomain.org and selector (e.g. 2021).

Record Name

Record Type

Text

2021._domainkey

TXT

v=DKIM1; k=rsa; p=MI.. (take it from /etc/dkimkeys/2021.txt file; remove the >"< and connect the lines after p= to one key.)

BIND Configuration : The public key file /etc/dkimkeys/2021.txt created by opendkim-genkey is in a format ready to be included in a DNS bind9 zone data file. It can be copied to a bind9 server, and pasted into a zone file, or the $INCLUDE statement may be used.

  • $INCLUDE /etc/dkimkeys/2021.txt

Testing

There are several checks one can perform to validate an OpenDKIM installation.

opendkim-testkey

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. A specific domain to check can be specified with optional "-d" option.

# opendkim-testkey -v -v
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: key loaded from /etc/dkimkeys/2021.private
opendkim-testkey: checking key '2021._domainkey.etbe.yourdomain.org'
opendkim-testkey: key not secure

In the example above "key not secure" means that DNSSEC is not in use (someone should add a DNSSEC section to the Bind9 page).

One issue with opendkim-testkey is that by default it (and the opendkim daemon) will do all its checks against external DNS servers, so it can't do anything useful on a test environment like an example.org 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

opendkim-testmsg

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.

email tests

In addition to the local testing commands listed above, a public DKIM configuration can be tested by sending an empty email to check-auth@verifier.port25.com . An automated reply should be received within a few minutes.

Another option may be https://www.mail-tester.com/ . Visit the website, email the test address, and click the website link to get feedback on junk email and DKIM ratings.

Mailman and DKIM Configuration

Mailman tends to break DKIM 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" canonicalization. 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.

See the Mailman DMARC Wiki page for a summary of the DMARC options and the Mailman DKIM Wiki page for a lot of background information.

FAQ

Q. OpenDKIM fails to start after upgrading from Debian 8 "jessie" to stretch
A) If OpenDKIM fails to start after upgrading from Debian 8 "jessie" to stretch it seems that you have to auto-generate the Systemd service file for OpenDKIM.
  • Run these commands:
/lib/opendkim/opendkim.service.generate
systemctl daemon-reload
service opendkim restart

See also

upstream specific information

Debian-specific information


CategoryNetwork CategoryMail CategorySoftware