From de330a87a44f1a4e4425965d2822cae7e7dabbaf Mon Sep 17 00:00:00 2001 From: Thomas Preisner Date: Sun, 29 Dec 2024 01:27:00 +0100 Subject: [PATCH] mailsystem: Add configuration options for dkim signatures --- mailsystem/default.nix | 39 ++++++++++++++++++++++++++++ mailsystem/rspamd.nix | 59 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/mailsystem/default.nix b/mailsystem/default.nix index aadfaad..c8eb8c4 100644 --- a/mailsystem/default.nix +++ b/mailsystem/default.nix @@ -168,6 +168,45 @@ in { default = {}; }; + dkimSettings = lib.mkOption { + type = with lib.types; + attrsOf (listOf (submodule { + options = { + selector = lib.mkOption { + type = lib.types.str; + example = "mail"; + description = "DKIM Selector"; + }; + keyFile = lib.mkOption { + type = lib.types.path; + example = "/run/secrets/dkim/example.com.mail.key"; + description = '' + Path to DKIM private-key-file. A public-private-pair can be generated as follows: + + ``` + nix-shell -p rspamd --run 'rspamadm dkim_keygen -s "selector" -t ed25519 -d example.com + nix-shell -p rspamd --run 'rspamadm dkim_keygen -s "selector" -b 2048 -d example.com + ``` + ''; + }; + }; + })); + example = { + "example.com" = [ + { + selector = "mail"; + keyFile = "/run/secrets/dkim/example.com.mail.key"; + } + ]; + }; + description = '' + Per-domain DKIM configuration. + This option allows to optionally set one or more DKIM private keys + and their respective selectors for each domain individually. + ''; + default = {}; + }; + certificateScheme = lib.mkOption { type = lib.types.enum ["acme" "selfsigned"]; default = "acme"; diff --git a/mailsystem/rspamd.nix b/mailsystem/rspamd.nix index 7f2a147..847aba4 100644 --- a/mailsystem/rspamd.nix +++ b/mailsystem/rspamd.nix @@ -39,12 +39,27 @@ in { }; config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = !cfg.rspamd.webUi.enable || cfg.rspamd.webUi.basicAuthFile != null; - message = "Setting basicAuthFile is required if rspamd's web interface is enabled"; - } - ]; + assertions = + [ + { + assertion = !cfg.rspamd.webUi.enable || cfg.rspamd.webUi.basicAuthFile != null; + message = "Setting basicAuthFile is required if rspamd's web interface is enabled"; + } + ] + ++ lib.mapAttrsToList ( + domain: dkimList: { + assertion = builtins.elem domain cfg.domains; + message = "Domain ${domain} as per `config.mailsystem.dkimSettings` needs to be managed by the mailserver."; + } + ) + cfg.dkimSettings + ++ lib.mapAttrsToList ( + domain: dkimList: { + assertion = dkimList != []; + message = "Entry ${domain} as per `config.mailsystem.dkimSettings` must not be an empty list."; + } + ) + cfg.dkimSettings; services.rspamd = { enable = true; @@ -57,6 +72,38 @@ in { } ''; }; + "dkim_signing.conf" = let + genDkimSelectorList = entry: '' + { + path: "${entry.keyFile}"; + selector: "${entry.selector}"; + } + ''; + genDkimDomainCfg = domain: domainSettings: '' + ${domain} { + selectors [ + ${lib.concatStringsSep "\n" (map genDkimSelectorList domainSettings)} + ] + } + ''; + in { + text = + '' + sign_authenticated = true; + use_esld = true; + use_domain = "header"; + check_pubkey = true; + allow_username_mismatch = true; + allow_hdrfrom_mismatch = true; + allow_hdrfrom_mismatch_sign_networks = true; + + '' + + lib.optionalString (cfg.dkimSettings != {}) '' + domain { + ${lib.concatStringsSep "\n" (lib.mapAttrsToList genDkimDomainCfg cfg.dkimSettings)} + } + ''; + }; "milter_headers.conf" = { text = '' # Add headers related to spam-detection