From caf9e51e0f836a14f34acafef3cde2e608f15f33 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sun, 14 Jan 2024 21:07:42 +0100 Subject: [PATCH] nixos/mail/dovecot2: re-introduce extra settings and rename `sieveScripts` https://github.com/NixOS/nixpkgs/pull/275031 introduced structured configuration for the dovecot2 sieve plugin, by doing so, it broke SNM configuration doing Sieve configurations. This attempts to fix up the public API to make it possible for SNM to pick up the pieces. --- nixos/modules/services/mail/dovecot.nix | 89 +++++++++++++++++++------ 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix index 25c7017a1d25..13f3e337cec2 100644 --- a/nixos/modules/services/mail/dovecot.nix +++ b/nixos/modules/services/mail/dovecot.nix @@ -4,7 +4,8 @@ let inherit (lib) any attrValues concatMapStringsSep concatStrings concatStringsSep flatten imap1 isList literalExpression mapAttrsToList mkEnableOption mkIf mkOption mkRemovedOptionModule optional optionalAttrs - optionalString singleton types; + optionalString singleton types mkRenamedOptionModule nameValuePair + mapAttrs' listToAttrs; cfg = config.services.dovecot2; dovecotPkg = pkgs.dovecot; @@ -12,6 +13,30 @@ let baseDir = "/run/dovecot2"; stateDir = "/var/lib/dovecot"; + sieveScriptSettings = mapAttrs' (to: from: nameValuePair "sieve_${to}" "${stateDir}/sieve/${from}") cfg.sieve.scripts; + imapSieveMailboxSettings = listToAttrs (flatten (imap1 (idx: el: + singleton { + name = "imapsieve_mailbox${toString idx}_name"; + value = el.name; + } ++ optional (el.from != null) { + name = "imapsieve_mailbox${toString idx}_from"; + value = el.from; + } ++ optional (el.causes != []) { + name = "imapsieve_mailbox${toString idx}_causes"; + value = concatStringsSep "," el.causes; + } ++ optional (el.before != null) { + name = "imapsieve_mailbox${toString idx}_before"; + value = "file:${stateDir}/imapsieve/before/${baseNameOf el.before}"; + } ++ optional (el.after != null) { + name = "imapsieve_mailbox${toString idx}_after"; + value = "file:${stateDir}/imapsieve/after/${baseNameOf el.after}"; + } + ) cfg.imapsieve.mailbox)); + sievePipeBinScriptDirectory = pkgs.linkFarm "sieve-pipe-bins" (map (el: { + name = builtins.unsafeDiscardStringContext (baseNameOf el); + path = el; + }) cfg.sieve.pipeBins); + dovecotConf = concatStrings [ '' base_dir = ${baseDir} @@ -77,14 +102,6 @@ let '' ) - ( - optionalString (cfg.sieveScripts != {}) '' - plugin { - ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieveScripts)} - } - '' - ) - ( optionalString (cfg.mailboxes != {}) '' namespace inbox { @@ -116,11 +133,16 @@ let '' ) + # General plugin settings: + # - sieve is mostly generated here, refer to `pluginSettings` to follow + # the control flow. '' plugin { + ${concatStringsSep "\n" (mapAttrsToList (to: from: "sieve_${to} = ${stateDir}/sieve/${to}") cfg.sieve.scripts)} sieve_plugins = ${concatStringsSep " " cfg.sieve.plugins} sieve_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions)} sieve_global_extensions = ${concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions)} + ${concatStringsSep "\n" (mapAttrsToList (key: value: "${key} = ${value}") cfg.sieve.extraPluginSettings)} '' (optionalString (cfg.imapsieve.mailbox != []) '' ${ @@ -199,6 +221,7 @@ in { imports = [ (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "") + (mkRenamedOptionModule [ "services" "dovecot2" "sieveScripts" ] [ "services" "dovecot2" "sieve" "scripts" ]) ]; options.services.dovecot2 = { @@ -337,12 +360,6 @@ in enableDHE = mkEnableOption (lib.mdDoc "ssl_dh and generation of primes for the key exchange") // { default = true; }; - sieveScripts = mkOption { - type = types.attrsOf types.path; - default = {}; - description = lib.mdDoc "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; - }; - showPAMFailure = mkEnableOption (lib.mdDoc "showing the PAM failure message on authentication error (useful for OTPW)"); mailboxes = mkOption { @@ -376,6 +393,26 @@ in description = lib.mdDoc "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %."; }; + + pluginSettings = mkOption { + # types.str does not coerce from packages, like `sievePipeBinScriptDirectory`. + type = types.attrsOf (types.oneOf [ types.str types.package ]); + default = {}; + example = literalExpression '' + { + sieve = "file:~/sieve;active=~/.dovecot.sieve"; + } + ''; + description = '' + Plugin settings for dovecot in general, e.g. `sieve`, `sieve_default`, etc. + + Some of the other knobs of this module will influence by default the plugin settings, but you + can still override any plugin settings. + + If you override a plugin setting, its value is cleared and you have to copy over the defaults. + ''; + }; + imapsieve.mailbox = mkOption { default = []; description = "Configure Sieve filtering rules on IMAP actions"; @@ -462,6 +499,12 @@ in type = types.listOf types.str; }; + scripts = mkOption { + type = types.attrsOf types.path; + default = {}; + description = lib.mdDoc "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; + }; + pipeBins = mkOption { default = []; example = literalExpression '' @@ -476,7 +519,6 @@ in }; }; - config = mkIf cfg.enable { security.pam.services.dovecot2 = mkIf cfg.enablePAM {}; @@ -501,6 +543,13 @@ in ++ optional (cfg.sieve.pipeBins != []) "sieve_extprograms"; sieve.globalExtensions = optional (cfg.sieve.pipeBins != []) "vnd.dovecot.pipe"; + + pluginSettings = lib.mapAttrs (n: lib.mkDefault) ({ + sieve_plugins = concatStringsSep " " cfg.sieve.plugins; + sieve_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions); + sieve_global_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions); + sieve_pipe_bin_dir = sievePipeBinScriptDirectory; + } // sieveScriptSettings // imapSieveMailboxSettings); }; users.users = { @@ -556,7 +605,7 @@ in # the source file and Dovecot won't try to compile it. preStart = '' rm -rf ${stateDir}/sieve ${stateDir}/imapsieve - '' + optionalString (cfg.sieveScripts != {}) '' + '' + optionalString (cfg.sieve.scripts != {}) '' mkdir -p ${stateDir}/sieve ${concatStringsSep "\n" ( mapAttrsToList ( @@ -569,7 +618,7 @@ in fi ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}' '' - ) cfg.sieveScripts + ) cfg.sieve.scripts )} chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' '' @@ -615,8 +664,8 @@ in message = "dovecot is configured with showPAMFailure while enablePAM is disabled"; } { - assertion = cfg.sieveScripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null); - message = "dovecot requires mailUser and mailGroup to be set when sieveScripts is set"; + assertion = cfg.sieve.scripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null); + message = "dovecot requires mailUser and mailGroup to be set when `sieve.scripts` is set"; } ];