WIP: make DKIM selector configurable per domain

This commit is contained in:
Michael Lohmann 2025-04-15 07:20:46 +02:00
parent f3910436d1
commit 0ac1140bd2
4 changed files with 27 additions and 12 deletions

View File

@ -65,7 +65,6 @@ can stay up to date with bug fixes and updates.
### In the future ### In the future
* DKIM Signing * DKIM Signing
- [ ] Allow per domain selectors
- [ ] Allow passing DKIM signing keys - [ ] Allow passing DKIM signing keys
* Improve the Forwarding Experience * Improve the Forwarding Experience
- [ ] Support [ARC](https://en.wikipedia.org/wiki/Authenticated_Received_Chain) signing with [Rspamd](https://rspamd.com/doc/modules/arc.html) - [ ] Support [ARC](https://en.wikipedia.org/wiki/Authenticated_Received_Chain) signing with [Rspamd](https://rspamd.com/doc/modules/arc.html)

View File

@ -787,10 +787,17 @@ in
}; };
dkimSelector = mkOption { dkimSelector = mkOption {
type = types.str; type = with types; let
mapStrToDomains = (selector:
builtins.listToAttrs (map (domain: {
name = domain;
value = selector;
}) cfg.domains));
in coercedTo str mapStrToDomains (attrsOf str);
default = "mail"; default = "mail";
description = '' description = ''
The DKIM selector. The DKIM selectors. If you provide a string, it is applied to all
domains. Otherwise you can provide it per domain.
''; '';
}; };

View File

@ -20,5 +20,13 @@ in {
assertion = cfg.acmeCertificateName == cfg.fqdn; assertion = cfg.acmeCertificateName == cfg.fqdn;
message = "When the certificate scheme is not 'acme' (mailserver.certificateScheme != \"acme\"), it is not possible to define mailserver.acmeCertificateName"; message = "When the certificate scheme is not 'acme' (mailserver.certificateScheme != \"acme\"), it is not possible to define mailserver.acmeCertificateName";
} }
]; ] ++ lib.optionals cfg.dkimSigning (
let
missingDomains = builtins.filter (d: !(cfg.dkimSelector ? "${d}")) cfg.domains;
in [
{
assertion = missingDomains == [];
message = "Missing DKIM selector for domains: ${builtins.concatStringsSep ", " missingDomains}";
}
]);
} }

View File

@ -25,27 +25,28 @@ let
createDomainDkimCert = dom: createDomainDkimCert = dom:
let let
dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key"; dkim_selector = cfg.dkimSelector[dom];
dkim_txt = "${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.txt"; dkim_key = "${cfg.dkimKeyDirectory}/${dom}.${dkim_selector}.key";
dkim_txt = "${cfg.dkimKeyDirectory}/${dom}.${dkim_selector}.txt";
in in
'' ''
if [ ! -f "${dkim_key}" ] if [ ! -f "${dkim_key}" ]
then then
${pkgs.opendkim}/bin/opendkim-genkey -s "${cfg.dkimSelector}" \ ${pkgs.opendkim}/bin/opendkim-genkey -s "${dkim_selector}" \
-d "${dom}" \ -d "${dom}" \
--bits="${toString cfg.dkimKeyBits}" \ --bits="${toString cfg.dkimKeyBits}" \
--directory="${cfg.dkimKeyDirectory}" --directory="${cfg.dkimKeyDirectory}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.private" "${dkim_key}" mv "${cfg.dkimKeyDirectory}/${dkim_selector}.private" "${dkim_key}"
mv "${cfg.dkimKeyDirectory}/${cfg.dkimSelector}.txt" "${dkim_txt}" mv "${cfg.dkimKeyDirectory}/${dkim_selector}.txt" "${dkim_txt}"
chmod 644 "${dkim_txt}" chmod 644 "${dkim_txt}"
echo "Generated key for domain ${dom} selector ${cfg.dkimSelector}" echo "Generated key for domain ${dom} selector ${dkim_selector}"
fi fi
''; '';
createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains); createAllCerts = lib.concatStringsSep "\n" (map createDomainDkimCert cfg.domains);
keyTable = pkgs.writeText "opendkim-KeyTable" keyTable = pkgs.writeText "opendkim-KeyTable"
(lib.concatStringsSep "\n" (lib.flip map cfg.domains (lib.concatStringsSep "\n" (lib.flip map cfg.domains
(dom: "${dom} ${dom}:${cfg.dkimSelector}:${cfg.dkimKeyDirectory}/${dom}.${cfg.dkimSelector}.key"))); (dom: "${dom} ${dom}:${dkim_selector}:${cfg.dkimKeyDirectory}/${dom}.${dkim_selector}.key")));
signingTable = pkgs.writeText "opendkim-SigningTable" signingTable = pkgs.writeText "opendkim-SigningTable"
(lib.concatStringsSep "\n" (lib.flip map cfg.domains (dom: "${dom} ${dom}"))); (lib.concatStringsSep "\n" (lib.flip map cfg.domains (dom: "${dom} ${dom}")));
@ -56,7 +57,7 @@ in
config = mkIf (cfg.dkimSigning && cfg.enable) { config = mkIf (cfg.dkimSigning && cfg.enable) {
services.opendkim = { services.opendkim = {
enable = true; enable = true;
selector = cfg.dkimSelector; selector = cfg.dkimSelector; # FIXME: opendkim can only handle a single selector. Figure out how to do this…
keyPath = cfg.dkimKeyDirectory; keyPath = cfg.dkimKeyDirectory;
domains = "csl:${builtins.concatStringsSep "," cfg.domains}"; domains = "csl:${builtins.concatStringsSep "," cfg.domains}";
configFile = pkgs.writeText "opendkim.conf" ('' configFile = pkgs.writeText "opendkim.conf" (''