nixos/dovecot: add compatibility for Dovecot 2.4

Co-authored-by: Katalin Rebhan <me@dblsaiko.net>
This commit is contained in:
Jappie3
2026-04-11 01:11:33 +02:00
committed by Katalin Rebhan
co-authored by Katalin Rebhan
parent ad56a1d988
commit d49db5bd96
+170 -20
View File
@@ -43,6 +43,8 @@ let
splitString
traceSeq
types
versionAtLeast
versionOlder
;
cfg = config.services.dovecot2;
@@ -50,6 +52,8 @@ let
baseDir = "/run/dovecot2";
stateDir = "/var/lib/dovecot";
sievec = lib.getExe' cfg.package.passthru.dovecot_pigeonhole "sievec";
sieveScriptSettings = mapAttrs' (
to: _: nameValuePair "sieve_${to}" "${stateDir}/sieve/${to}"
) cfg.sieve.scripts;
@@ -89,9 +93,14 @@ let
);
allSieveSettings = mkMerge [
imapSieveMailboxSettings
sieveScriptSettings
{
sieve_pipe_bin_dir =
mkIf (cfg.sieve.pipeBins != [ ]) # .
sievePipeBinScriptDirectory;
}
(mkIf isPre24 imapSieveMailboxSettings)
(mkIf isPre24 {
sieve_plugins =
mkIf (cfg.sieve.plugins != [ ]) # .
(concatStringsSep " " cfg.sieve.plugins);
@@ -101,10 +110,7 @@ let
sieve_global_extensions =
mkIf (cfg.sieve.globalExtensions != [ ]) # .
(concatMapStringsSep " " (el: "+${el}") cfg.sieve.globalExtensions);
sieve_pipe_bin_dir =
mkIf (cfg.sieve.pipeBins != [ ]) # .
sievePipeBinScriptDirectory;
}
})
];
yesOrNo = v: if v then "yes" else "no";
@@ -153,10 +159,23 @@ let
else
throw (traceSeq v "services.dovecot2.settings: unexpected type");
doveConf = concatStringsSep "\n" (
optionals (cfg.includeFiles != [ ]) (map (f: "!include ${f}") cfg.includeFiles)
++ flatten (mapAttrsToList (formatKeyValue "") cfg.settings)
);
doveConf =
let
configVersion = cfg.settings.dovecot_config_version or null;
storageVersion = cfg.settings.dovecot_storage_version or null;
remainingSettings = builtins.removeAttrs cfg.settings [
"dovecot_config_version"
"dovecot_storage_version"
];
in
concatStringsSep "\n" (
optional (configVersion != null) (formatKeyValue "" "dovecot_config_version" configVersion)
++ optional (storageVersion != null) (formatKeyValue "" "dovecot_storage_version" storageVersion)
++ optionals (cfg.includeFiles != [ ]) (map (f: "!include ${f}") cfg.includeFiles)
++ flatten (mapAttrsToList (formatKeyValue "") remainingSettings)
);
isPre24 = versionOlder cfg.package.version "2.4";
in
{
imports = [
@@ -180,6 +199,31 @@ in
"dovecot2"
"enableLmtp"
] "Set 'services.dovecot2.settings.protocols.lmtp = true/false;' instead.")
(mkRemovedOptionModule [
"services"
"dovecot2"
"sslServerCert"
] "Use `settings.ssl_cert` for Dovecot 2.3, `settings.ssl_server_cert_file` for 2.4.")
(mkRemovedOptionModule [
"services"
"dovecot2"
"sslServerKey"
] "Use `settings.ssl_key` for Dovecot 2.3, `settings.ssl_server_key_file` for 2.4.")
(mkRemovedOptionModule [
"services"
"dovecot2"
"sslCACert"
] "Use `settings.ssl_ca` for Dovecot 2.3, `settings.ssl_server_ca_file` for 2.4.")
(mkRemovedOptionModule [
"services"
"dovecot2"
"mailLocation"
] "Use `settings.mail_location` for Dovecot 2.3, `settings.mail_path` for 2.4.")
(mkRemovedOptionModule [
"services"
"dovecot2"
"enableDHE"
] "Use ECDHE instead, or use recommended parameters from RFC7919.")
(mkRenamedOptionModule
[ "services" "dovecot2" "sieveScripts" ]
[ "services" "dovecot2" "sieve" "scripts" ]
@@ -223,6 +267,9 @@ in
enable = mkEnableOption "the dovecot 2.x POP3/IMAP server";
package = mkPackageOption pkgs "dovecot" { } // {
default =
if versionAtLeast config.system.stateVersion "26.05" then pkgs.dovecot else pkgs.dovecot_2_3;
defaultText = lib.literalExpression ''if versionAtLeast config.system.stateVersion "26.05" then pkgs.dovecot else pkgs.dovecot_2_3'';
};
settings = mkOption {
@@ -440,7 +487,13 @@ in
imapsieve.mailbox = mkOption {
default = [ ];
description = "Configure Sieve filtering rules on IMAP actions";
description = ''
Configure Sieve filtering rules on IMAP actions
::: {.note}
This option is no longer used starting from Dovecot version 2.4.
:::
'';
type = types.listOf (
types.submodule (
{ config, ... }:
@@ -566,13 +619,15 @@ in
security.pam.services.dovecot2 = mkIf cfg.enablePAM { };
services.dovecot2 = {
sieve.plugins =
sieve.plugins = mkIf isPre24 (
optional (cfg.imapsieve.mailbox != [ ]) "sieve_imapsieve"
++ optional (cfg.sieve.pipeBins != [ ]) "sieve_extprograms";
++ optional (cfg.sieve.pipeBins != [ ]) "sieve_extprograms"
);
sieve.globalExtensions = optional (cfg.sieve.pipeBins != [ ]) "vnd.dovecot.pipe";
sieve.globalExtensions = mkIf isPre24 (optional (cfg.sieve.pipeBins != [ ]) "vnd.dovecot.pipe");
settings = mkMerge [
# options shared between 2.3 and 2.4
{
base_dir = mkDefault baseDir;
sendmail_path = mkDefault "/run/wrappers/bin/sendmail";
@@ -582,13 +637,16 @@ in
default_internal_group = mkDefault cfg.group;
maildir_copy_with_hardlinks = mkDefault true;
pop3_uidl_format = mkDefault "%08Xv%08Xu";
pop3_uidl_format = if !isPre24 then mkDefault "%{uidvalidity}%{uid}" else mkDefault "%08Xv%08Xu";
auth_mechanisms = mkDefault "plain login";
"service auth" = {
user = "root";
};
}
# these options differ quite a bit between 2.3 and 2.4, which is why they were split up here
# for pre-2.4:
(mkIf isPre24 {
mail_location = mkDefault "maildir:/var/spool/mail/%u";
passdb = mkIf cfg.enablePAM (mkDefault {
@@ -603,8 +661,8 @@ in
mail_plugins = mkDefault "$mail_plugins ${concatStringsSep " " cfg.mailPlugins.globally.enable}";
plugin = allSieveSettings;
}
(mkIf (cfg.mailPlugins.perProtocol != { } || cfg.mailPlugins.globally.enable != [ ]) (
})
(mkIf (isPre24 && cfg.mailPlugins.perProtocol != { } || cfg.mailPlugins.globally.enable != [ ]) (
listToAttrs (
map (m: {
name = "protocol ${elemAt (splitString "." m.name) 0}";
@@ -614,6 +672,32 @@ in
}) (attrsToList cfg.mailPlugins.perProtocol)
)
))
# for 2.4:
(mkIf (!isPre24) {
mail_driver = mkDefault "maildir";
mail_path = mkDefault "/var/spool/mail/%{user}";
sieve_script_bin_path = "/tmp/dovecot-%{user|username|lower}";
"passdb pam" = mkIf cfg.enablePAM (mkDefault {
driver = "pam";
service_name = "dovecot2";
failure_show_msg = mkIf cfg.showPAMFailure true;
});
"userdb passwd" = mkIf cfg.enablePAM (mkDefault {
driver = "passwd";
});
sieve_plugins = mkIf (cfg.sieve.pipeBins != [ ]) {
"sieve_extprograms" = true;
};
sieve_global_extensions = mkIf (cfg.sieve.pipeBins != [ ]) {
"vnd.dovecot.pipe" = true;
};
})
(mkIf (!isPre24) allSieveSettings)
];
};
@@ -733,7 +817,7 @@ in
else
cp -p '${from}' '${stateDir}/sieve/${to}'
fi
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}'
${sievec} '${stateDir}/sieve/${to}'
'') cfg.sieve.scripts
)}
${optionalString (
@@ -747,11 +831,11 @@ in
el:
optionalString (el.before != null) ''
cp -p ${el.before} ${stateDir}/imapsieve/before/${baseNameOf el.before}
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/before/${baseNameOf el.before}'
${sievec} '${stateDir}/imapsieve/before/${baseNameOf el.before}'
''
+ optionalString (el.after != null) ''
cp -p ${el.after} ${stateDir}/imapsieve/after/${baseNameOf el.after}
${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/after/${baseNameOf el.after}'
${sievec} '${stateDir}/imapsieve/after/${baseNameOf el.after}'
''
) cfg.imapsieve.mailbox}
@@ -763,7 +847,73 @@ in
environment.systemPackages = [ cfg.package ];
warnings = optional isPre24 ''
While Dovecot 2.3 is not yet deprecated or EOL,
there is a newer version available in Nixpkgs (Dovecot 2.4).
Check https://doc.dovecot.org/latest/installation/upgrade/2.3-to-2.4.html
before upgrading.
'';
assertions = [
{
assertion = isPre24 || cfg.settings.dovecot_config_version != null;
message = ''
services.dovecot2: Since Dovecot 2.4, the option 'services.dovecot2.settings.dovecot_config_version' must be explicitly set.
To retain compatibility with future updates, set the following and manually update as needed.
services.dovecot2.settings.dovecot_config_version = "${cfg.package.version}";
Alternatively, you can automatically update to newer versions of the configuration format, which might break compatibility with future updates.
services.dovecot2.settings.dovecot_config_version = config.services.dovecot2.package.version;
See <https://doc.dovecot.org/latest/installation/upgrade/2.3-to-2.4.html>.
'';
}
{
assertion = isPre24 || cfg.settings.dovecot_storage_version != null;
message = ''
services.dovecot2: Since Dovecot 2.4, the option 'services.dovecot2.settings.dovecot_storage_version' must be explicitly set.
Set it to the oldest version the storage should stay compatible with, for example the following for the currently selected version.
services.dovecot2.settings.dovecot_storage_version = "${cfg.package.version}";
See <https://doc.dovecot.org/latest/installation/upgrade/2.3-to-2.4.html>.
'';
}
{
assertion = isPre24 || cfg.imapsieve.mailbox == [ ];
message = "since Dovecot 2.4, the `imapsieve.mailbox` option is no longer used in the NixOS module, please use the `settings` option instead.";
}
{
assertion = isPre24 || cfg.sieve.extensions == [ ];
message = ''
services.dovecot2: Since Dovecot 2.4, the option 'services.dovecot2.sieve.extensions' is no longer valid, but it is set.
Set 'services.dovecot2.settings.sieve_extensions' instead.
See <https://doc.dovecot.org/latest/core/summaries/settings.html#sieve_extensions>.
'';
}
{
assertion = isPre24 || cfg.sieve.globalExtensions == [ ];
message = ''
services.dovecot2: Since Dovecot 2.4, the option 'services.dovecot2.sieve.globalExtensions' is no longer valid, but it is set.
Set 'services.dovecot2.settings.sieve_global_extensions' instead.
See <https://doc.dovecot.org/latest/core/summaries/settings.html#sieve_global_extensions>.
'';
}
{
assertion =
isPre24
||
# this is the default value for cfg.mailPlugins
cfg.mailPlugins == {
globally = {
enable = [ ];
};
perProtocol = { };
};
message = "since Dovecot 2.4, the `mailPlugins` option is no longer used in the NixOS module, please use the `settings` option instead.";
}
{
assertion = cfg.showPAMFailure -> cfg.enablePAM;
message = "dovecot is configured with showPAMFailure while enablePAM is disabled";