Login Logout

Howto OpenDKIM

DKIM (DomainKeys Identified Mail) est une norme qui décrit l’ajout d’une signature cryptographique dans les en-têtes d’un email envoyé.

DKIM signe les mails avec une clé privée stockée sur le serveur. La clé publique est mise à disposition dans un enregistrement DNS TXT et permet au destinataire de vérifier que le domaine contenu dans l’adresse d’expédition appartient bien à l’expéditeur.

La signature se fait à partir à de l’expéditeur contenu dans l’en-tête (From:), d’autres en-têtes au choix (sujet, date, etc.) et du corps du message.

OpenDKIM est logiciel libre permettant de vérifier et générer des signatures DKIM. Il implémente un service milter ( mail + filter ) lui permettant notamment d’être utilisé avec Postfix.

La norme DKIM remplace DomainKey obsolète (utilisée à l’origine par Yahoo).

Installation

# apt install opendkim opendkim-tools
# adduser opendkim ssl-cert

Créer /etc/systemd/system/opendkim.service.d/override.conf afin que le service ne se relance pas trop rapidement :

[Service]
RestartSec=5s
# opendkim -V | head -4
opendkim: OpenDKIM Filter v2.11.0
        Compiled with OpenSSL 1.1.0f  25 May 2017
        SMFI_VERSION 0x1000001
        libmilter version 1.0.1

# systemctl status opendkim
● opendkim.service - OpenDKIM DomainKeys Identified Mail (DKIM) Milter
   Loaded: loaded (/lib/systemd/system/opendkim.service; enabled; vendor preset: enabled)
  Drop-In: /etc/systemd/system/opendkim.service.d
           └─override.conf
   Active: active (running) since Mon 2023-07-03 11:41:31 CEST; 19s ago
     Docs: man:opendkim(8)
           man:opendkim.conf(5)
           man:opendkim-genkey(8)
           man:opendkim-genzone(8)
           man:opendkim-testadsp(8)
           man:opendkim-testkey
           http://www.opendkim.org/docs.html
  Process: 18085 ExecStart=/usr/sbin/opendkim -x /etc/opendkim.conf (code=exited, status=0/SUCCESS)
 Main PID: 18086 (opendkim)
    Tasks: 6 (limit: 4915)
   CGroup: /system.slice/opendkim.service
           └─18086 /usr/sbin/opendkim -x /etc/opendkim.conf

Configuration

La configuration principale se trouve dans le fichier /etc/opendkim.conf.

Note 1 : Attention, /etc/opendkim.conf et les fichiers dans /etc/opendkim/ doivent appartenir à l’utilisateur et au groupe opendkim.

Note 2 : Depuis Debian 9, l’unité systemd charge seulement /etc/opendkim.conf et non plus /etc/default/opendkim.

Les options de configuration se combinent principalement sur deux modes :

  • Configuration par domaine d’expédition (conseillée) :
    • /etc/opendkim/KeyTable : liste des clés privées / sélecteurs
    • /etc/opendkim/SigningTable : clés privées à utiliser en fonction de l’adresse From:
  • Configuration avec une seule clé privée (méthode dépréciée chez Evolix) :
    • /etc/opendkim/Domain : liste des domaines acceptés
    • /etc/opendkim/KeyFile : clé privée
    • /etc/opendkim/Selector : nom de la clé dans l’enregistrement DNS
    • Pas de KeyTable ni de SigningTable

Configuration par domaine d’expédition, avec une ou plusieurs clés privées

# cat /etc/opendkim.conf

UserID                  opendkim:opendkim
Syslog                  yes
#SyslogSuccess          yes
#LogWhy                 yes
UMask                   007
InternalHosts           /etc/opendkim/dkim.peers
Mode                    sv
OversignHeaders         From
#DisableADSP             true
Socket                  inet:8891@localhost
PidFile                 /var/run/opendkim/opendkim.pid
RemoveOldSignatures     yes

SigningTable            refile:/etc/opendkim/SigningTable
KeyTable                refile:/etc/opendkim/KeyTable

Le fichier /etc/opendkim/KeyTable définit les clés privées et leur sélecteur :

key1 example.com:key1_selector:/etc/ssl/private/dkim_example.com.key
key2 example.org:key2_selector:/etc/ssl/private/dkim_example.org.key

Un sélecteur est une expression qui sert aux clients à trouver le sous-domaine de l’enregistrement TXT qui contient la clé publique DKIM du domaine (via <SELECTOR>._domainkey.example.com).

Si on a plusieurs serveurs de messagerie (et donc plusieurs clé privées), des sélecteurs différents permettent d’avoir plusieurs clés publiques associées à un même domaine, dans des enregistrements TXT de différents sous-domaines.

On peut utiliser un même sélecteur et clé privée pour plusieurs domaines (pratique sur les serveurs avec beaucoup de domaines) ainsi :

key1 %:key1_selector:/etc/ssl/private/dkim_my_mail_server.key

Le fichier /etc/opendkim/SigningTable indique quelle clé utiliser pour signer en fonction du champ From: :

*@example.com key1
*@example.org key2

Les lignes sont traitées dans l’ordre, seul le premier match est utilisé, sauf si MultipleSignatures est activé.

Pour générer une clé, on utilise la commande :

# opendkim-genkey -h sha256 -b 4096 -D /etc/ssl/private/ -d example.com -s foo -v
opendkim-genkey: generating private key
opendkim-genkey: private key written to foo.private
opendkim-genkey: extracting public key
opendkim-genkey: DNS TXT record written to foo.txt

# mv /etc/ssl/private/foo.private /etc/ssl/private/dkim_example.com.key
# chown opendkim:opendkim /etc/ssl/private/dkim_example.com.key
# chmod 640 /etc/ssl/private/dkim_example.com.key

Il faut ensuite publier l’enregistrement DNS à partir du fichier /etc/ssl/private/foo.txt généré, en ajoutant la ligne suivante dans la zone DNS du domaine en question :

foo._domainkey  IN      TXT     ( "v=DKIM1; h=sha256; k=rsa; "
          "p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtdnoCyrwNyCAttQz0VaJq3cheWtki8KTfCwPu+QDcyv8IHLpQGK2UsEw03epEjTI/ijdyyVsElDPoqsCZ4+R3H5I41Vj8xcGLvqIaG3mbgOhcxuD+eEPR1K+JbSptwMiP/oja7VFyftQtx5XkQh8oj458WR/EE++SzjOGXOVmFYzf+LnNXJ/Rc0avAUPcTF1NmoU8IP52aKTvw"
          "nrbPFhIPkUyIR1/5k+5h4ZJqY+2h+JqDuol0E73Yl79rsF5ycjwLtBCPsz80jzCpOF5YHkDx0CYPyTKoIA3eZlgOVSJPmDYUWCrj/9k2bL/nSL5EJoB/X/kQcLRTy83dqf+gzWd850yP9vpUQQ2d+Z1hdlAFvzVMboSNz12+au7kAglXmN47l9Z8igxVWJfl7BEsegQF8gYPB4yepSW3DtfxBC5zCjLYYN1xXabf3wZfeCF6yOoQk1C2yN"
          "ThvBa4NyQDWizZGs4t9eHTR8QHMzJogPQQvyS45ILN1HHeikIQZP/lormmFQzq9X8sDGt4Edy8A3OMLjHom81tP6zxb4I8Pq0V4bEt4m4KA+K89A+b9cTD2Xr/wbqgQ1nwTcnEfGgBxbc3iRKkUnwhjeIocsxmwHqmDIVV8HKB5egUg8US/eo9al8w4JYEdzx9tETW/5dQOeLTMVw2N0/A7M9zVbBQEyuaMCAwEAAQ==" )  ; ----- DKIM key foo for example.com

On utilise le fichier /etc/opendkim/dkim.peers pour mettre la liste des adresses IP autorisées à envoyer des messages pour lesquels on va rajouter une signature DKIM (a priori des serveurs SMTP internes uniquement) :

127.0.0.1
::1
192.0.2.142
192.0.2.0/25

On ajuste les droits ainsi :

# chown -R opendkim:opendkim /etc/opendkim*
# chmod 640 /etc/opendkim.conf /etc/opendkim/*
# chmod 750 /etc/opendkim/

Et l’on peut enfin démarrer le démon :

# systemctl start opendkim

Configuration avec une seule clé privée (deprecated)

Générer une paire de clés avec le sélecteur dkim-<SERVER_NAME> (par exemple) dans le répertoire /etc/opendkim/keys/ :

# mkdir /etc/opendkim/keys
# sudo -u opendkim opendkim-genkey -h sha256 -b 4096 -D /etc/opendkim/keys/ -s dkim-<SERVER_NAME>

Cela met la clé privée dans /etc/opendkim/keys/dkim-<SERVER_NAME>.private, et l’enregistrement DNS TXT contenant la clé publique dans /etc/opendkim/keys/dkim-<SERVER_NAME>.txt

Créer les whitelistes :

# vim /etc/opendkim/domains_whitelist
+ # Indiquez ici les domaines pour lesquels OpenDKIM va signer les mails.
# vim /etc/opendkim/hosts_whitelist
+ # Indiquez ici les IPs/sous-réseaux des serveurs SMTP internes pour lesquels OpenDKIM va signer les mails.
+ 127.0.0.1
+ ::1

Ajuster la configuration générale dans /etc/opendkim.conf :

UserID                  opendkim:opendkim
Syslog                  yes
#SyslogSuccess          yes
#LogWhy                 yes
UMask                   007
Mode                    sv
OversignHeaders         From
#DisableADSP             true
Socket                  inet:8891@localhost
PidFile                 /var/run/opendkim/opendkim.pid
RemoveOldSignatures     yes

# Liste les IPs des serveurs SMTP internes pour lesquels OpenDKIM va signer les mails
InternalHosts           refile:/etc/opendkim/hosts_whitelist
ExternalIgnoreList      refile:/etc/opendkim/hosts_whitelist

# Clé privée (générée avec "sudo -u opendkim opendkim-genkey -h sha256 -b 4096 -D /etc/opendkim/keys/ -s dkim-<SERVER_NAME>")
KeyFile                 /etc/opendkim/keys/dkim-<SERVER_NAME>.private
# Utilisé dans l'enregistrement DNS utilisé pour récupérer la clé publique ($selector._domainkey.$domain)
Selector                dkim-<SERVER_NAME>
# Liste des domaines pour lesquels OpenDKIM va signer les mails (accepte les regex)
Domain                  refile:/etc/opendkim/domains_whitelist

Ajouter un domaine à OpenDKIM

Ajouter le texte contenu dans le fichier /etc/opendkim/keys/dkim-<SERVER_NAME>.txt aux enregistrements DNS du domaine. Cela va rendre la clé publique accessible via un enregistrement TXT du sous-domaine dkim-<SERVER_NAME>._domainkey.$domain.

On peut vérifier l’enregistrement DNS avec :

$ host -t TXT dkim-<SERVER_NAME>._domainkey.$domain

Puis, ajouter le domaine à la liste /etc/opendkim/domains_whitelist et redémarrer le service :

# vim /etc/opendkim/domains_whitelist
+ $domain
# systemctl restart opendkim
# systemctl status opendkim

Ajout dans Postfix

Pour configurer avec Postfix on ajoute simplement les instructions suivantes dans le fichier /etc/postfix/main.cf :

smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
#milter_default_action=accept
# Pour accélérer les signatures, pas d'attente pour contacter le milter
in_flow_delay = 0s

Cela entraînera l’ajout d’un en-tête DKIM-Signature: à tous les messages qui correspondent à la configuration d’OpenDKIM.

Si vous utilisez l’option Mode sv ou Mode v dans la configuration d’OpenDKIM, cela entraînera une vérification des signatures DKIM des emails reçus, et l’ajout d’un en-tête du type :

Authentication-Results: antispam.example.com; dkim=pass
        reason="1024-bit key; unprotected key"
        header.d=example.com header.i=@example.com header.b=lJ7oU+Kk;
        dkim-adsp=pass; dkim-atps=neutral

Attention, si vous utilisez aussi Amavis, vérifiez bien que /etc/postfix/master.cf a bien l’option no_milters dans son option -o receive_override_options pour le process SMTPD de retour d’Amavis (en général 127.0.0.1:10025) sinon OpenDKIM pourrait signer aussi les emails entrants au retour d’Amavis (cela dépend évidemment de votre SigningTable) :

opendkim[18086]: 8A836229A8: mail-pl1-f202.google.com [209.85.214.202] not internal
opendkim[18086]: 8A836229A8: not authenticated
opendkim[18086]: 2760F22B59: DKIM-Signature field added (s=dkim-foo, d=google.com)

Vérifications

Vérifier la configuration

On peut vérifier que la configuration est correcte, notamment l’enregistrement DNS (si la commande ne renvoie rien, c’est que c’est OK) :

# opendkim-testkey -d example.com -s foo -k /etc/ssl/private/dkim_example.com.key -v
opendkim-testkey: /etc/ssl/private/dkim_example.com.key: WARNING: unsafe permissions
opendkim-testkey: key not secure

Note : vous pouvez ignorer le warning des permissions si vous avez correctement ajusté les droits comme précisé plus haut

Vérifier la signature DKIM d’un mail

Il faut envoyer un email de test avec un expéditeur d’en-tête correct. Par exemple, en ligne de commande :

$ telnet 127.0.0.1 25
HELO example.com
MAIL FROM: <noreply@example.com>
RCPT TO: <foo@example.org>
DATA
From: <noreply@example.com>
To: Foo <foo@example.org>
Subject: test DKIM

Ceci est un test
.

La source de l’email devrait avoir un en-tête supplémentaire du type :

DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=example.com; s=foo;
    t=1541094902; bh=xj6JF8HXzDkYeJ72cJNmx7FnTp5CeDo6y+MwRwIeNC4=;
    h=Date:From:To:Subject:From;
    b=lJ8oU+KkUBWEWVl/HpztFx5Hdv2rhqLP03JfJ1yqp3+TR3aZX+uOCI4MKqvonYzXM
     kHl4YYsYT7fSnuSaF48feGbGQ1AauGKd2x9UKcBEBQq2cGLG7ahfXph/SF5eh7/psL
     dXal6gCw6416bJQMBa9sb+zSA/9Avy3zQ0jGCALw=

Dans Thunderbird, le plugin DKIM verifier permet de vérifier les en-têtes DKIM des mails reçus.

Sinon, si vous avez accès à un email Gmail ou Yahoo, vous pouvez lui envoyer un email. En affichant le message original, vous pourrez vérifier la présence de DKIM-Signature:. Cela vous indiquera si la signature est correcte et acceptée.

Vérifier via des services externes

Monitoring

Il est important de s’assurer que le démon opendkim tourne en permanence.

Nagios

Voici un check basique pour vérifier que le port TCP d’opendkim répond bien :

$ /usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 8891

log2mail

Pour être alerté en cas de *warning: connect to Milter service inet:8891: Connection refused on ajoute la configuration suivante au logiciel log2mail :

file = /var/log/syslog
  pattern = "warning: connect to Milter service inet:8891: Connection refused"
  mailto = alert@example.com

FAQ

opendkim: smfi_opensocket() failed

Si vous obtenez le message suivant au démarrage :

Starting OpenDKIM Milter: opendkim: smfi_opensocket() failed

C’est que la socket réseau est déjà occupée !

ADSP (Author Domain Signing Practices)

**ADSP est désormais obsolète, et remplacé par la spécification DMARC

ADSP est une extension à DKIM permettant d’indiquer le traitement à effectuer en cas de signature DKIM invalide ou absente.

Si l’on est sûr que tous les mails émis avec le nom de domaine utilisé sont bien signés avec DKIM, on peut l’indiquer via l’enregistrement DNS suivant, ce qui provoquera la pénalisation des emails sans signature :

_adsp._domainkey        IN      TXT "dkim=all"

On peut vérifier que l’enregistrement ADSP est bien pris en compte avec la commande suivante :

# opendkim-testadsp example.com

Pas d’en-tête DKIM-Signature

Si vous ne constatez pas l’ajout de la signature DKIM, vérifiez votre configuration, vérifiez que vous utilisez bien un champ From: correct et correspondant à un domaine à signer. Si besoin activé le « mode debug », cf ci-dessous.

Logs de signature (mode debug)

Il peut aussi être intéressant d’activer les options suivantes dans /etc/opendkim.conf pour avoir davantage de logs :

Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes

On verra ainsi des logs dans /var/log/mail.log du type :

opendkim[18086]: E4DB722DFA: mail-ej1-f53.google.com [209.85.218.53] not internal
opendkim[18086]: E4DB722DFA: not authenticated

opendkim[18086]: 18F02230B9: DKIM-Signature field added (s=dkim-foo, d=example.com)

Logs de vérification (mode debug)

Il peut être intéressant d’activer l’option suivante /etc/opendkim.conf` pour avoir des logs pour chaque signature vérifiée par opendkim :

LogResults true

Requêtes DNS externes pour ADSP

Attention, par défaut OpenDKIM effectue ses vérifications ADSP en faisant de multiples requêtes DNS sans utiliser le resolver local, ce qui qui peut provoquer des ralentissements notamment si cela n’est pas autorisé au niveau firewall. Nous conseillons de désactiver ces vérifications qui sont obsolètes :

DisableADSP true

Gmail/Google et DKIM

Informations de Gmail/Google à propos de DKIM : https://support.google.com/a/answer/174124?hl=fr.

Problème de header (dkim-filter: no sender header found)

Avec dkim-filter, en Debian 7, il se peux que, suite a une règle de ce type dans postfix :

/^received:/ IGNORE

cela casse dkim-filter, car il ne vois pas si le sender est correct dans le header du mail, et donc il ne sais pas si c’est un domaine qu’il doit signé ou pas.

Regenérer la zone DNS

Lorsqu’une clé a déjà été générée mais que le fichier contenant la zone DNS n’est plus disponible, il est possible de la régénérer avec la commande suivante :

opendkim-genzone -F -d example.com  /etc/opendkim/KeyTable

body hash did not verify

Cette erreur vient du fait que le payload du message a été modifié par un serveur SMTP entre le départ et l’arrivée (inclus). Cela peut venir de lignes trop longues (cf plus bas).

Lignes trop longues

D’après https://www.rfc-editor.org/rfc/rfc2822#section-2.1.1 les lignes d’un email ne doivent pas être trop longues sinon elles peuvent se faire découper par un serveur SMTP entre le départ et l’arrivée (inclus)… ce qui changer le payload du message car il aura notamment des sauts de ligne en plus.. et donc le body hash de la signature DKIM ne sera plus valide.

Signer tous les emails avec la même clé ?

Pour signer tous les domaines avec un même clé, on peut mettre un wildcard dans /etc/opendkim/SigningTable :

* key1

Mais c’est déconseillé car tous les emails car OpenDKIM tenteraient de traiter tous les domaines, et cela peut poser des problèmes avec des emails tordus (par exemple OpenDKIM plante quand on trouve une espace sans chasse dans le nom de domaine)