at 24.11-pre 24 kB view raw
1{ config, lib, pkgs, ... }: 2 3let 4 inherit (lib) attrValues concatMapStringsSep concatStrings 5 concatStringsSep flatten imap1 literalExpression mapAttrsToList 6 mkEnableOption mkIf mkOption mkRemovedOptionModule optional optionalAttrs 7 optionalString singleton types mkRenamedOptionModule nameValuePair 8 mapAttrs' listToAttrs filter; 9 inherit (lib.strings) match; 10 11 cfg = config.services.dovecot2; 12 dovecotPkg = pkgs.dovecot; 13 14 baseDir = "/run/dovecot2"; 15 stateDir = "/var/lib/dovecot"; 16 17 sieveScriptSettings = mapAttrs' (to: _: nameValuePair "sieve_${to}" "${stateDir}/sieve/${to}") cfg.sieve.scripts; 18 imapSieveMailboxSettings = listToAttrs (flatten (imap1 (idx: el: 19 singleton { 20 name = "imapsieve_mailbox${toString idx}_name"; 21 value = el.name; 22 } ++ optional (el.from != null) { 23 name = "imapsieve_mailbox${toString idx}_from"; 24 value = el.from; 25 } ++ optional (el.causes != []) { 26 name = "imapsieve_mailbox${toString idx}_causes"; 27 value = concatStringsSep "," el.causes; 28 } ++ optional (el.before != null) { 29 name = "imapsieve_mailbox${toString idx}_before"; 30 value = "file:${stateDir}/imapsieve/before/${baseNameOf el.before}"; 31 } ++ optional (el.after != null) { 32 name = "imapsieve_mailbox${toString idx}_after"; 33 value = "file:${stateDir}/imapsieve/after/${baseNameOf el.after}"; 34 } 35 ) cfg.imapsieve.mailbox)); 36 37 mkExtraConfigCollisionWarning = term: '' 38 You referred to ${term} in `services.dovecot2.extraConfig`. 39 40 Due to gradual transition to structured configuration for plugin configuration, it is possible 41 this will cause your plugin configuration to be ignored. 42 43 Consider setting `services.dovecot2.pluginSettings.${term}` instead. 44 ''; 45 46 # Those settings are automatically set based on other parts 47 # of this module. 48 automaticallySetPluginSettings = [ 49 "sieve_plugins" 50 "sieve_extensions" 51 "sieve_global_extensions" 52 "sieve_pipe_bin_dir" 53 ] 54 ++ (builtins.attrNames sieveScriptSettings) 55 ++ (builtins.attrNames imapSieveMailboxSettings); 56 57 # The idea is to match everything that looks like `$term =` 58 # but not `# $term something something` 59 # or `# $term = some value` because those are comments. 60 configContainsSetting = lines: term: (match "^[^#]*\b${term}\b.*=" lines) != null; 61 62 warnAboutExtraConfigCollisions = map mkExtraConfigCollisionWarning (filter (configContainsSetting cfg.extraConfig) automaticallySetPluginSettings); 63 64 sievePipeBinScriptDirectory = pkgs.linkFarm "sieve-pipe-bins" (map (el: { 65 name = builtins.unsafeDiscardStringContext (baseNameOf el); 66 path = el; 67 }) cfg.sieve.pipeBins); 68 69 dovecotConf = concatStrings [ 70 '' 71 base_dir = ${baseDir} 72 protocols = ${concatStringsSep " " cfg.protocols} 73 sendmail_path = /run/wrappers/bin/sendmail 74 # defining mail_plugins must be done before the first protocol {} filter because of https://doc.dovecot.org/configuration_manual/config_file/config_file_syntax/#variable-expansion 75 mail_plugins = $mail_plugins ${concatStringsSep " " cfg.mailPlugins.globally.enable} 76 '' 77 78 ( 79 concatStringsSep "\n" ( 80 mapAttrsToList ( 81 protocol: plugins: '' 82 protocol ${protocol} { 83 mail_plugins = $mail_plugins ${concatStringsSep " " plugins.enable} 84 } 85 '' 86 ) cfg.mailPlugins.perProtocol 87 ) 88 ) 89 90 ( 91 if cfg.sslServerCert == null then '' 92 ssl = no 93 disable_plaintext_auth = no 94 '' else '' 95 ssl_cert = <${cfg.sslServerCert} 96 ssl_key = <${cfg.sslServerKey} 97 ${optionalString (cfg.sslCACert != null) ("ssl_ca = <" + cfg.sslCACert)} 98 ${optionalString cfg.enableDHE ''ssl_dh = <${config.security.dhparams.params.dovecot2.path}''} 99 disable_plaintext_auth = yes 100 '' 101 ) 102 103 '' 104 default_internal_user = ${cfg.user} 105 default_internal_group = ${cfg.group} 106 ${optionalString (cfg.mailUser != null) "mail_uid = ${cfg.mailUser}"} 107 ${optionalString (cfg.mailGroup != null) "mail_gid = ${cfg.mailGroup}"} 108 109 mail_location = ${cfg.mailLocation} 110 111 maildir_copy_with_hardlinks = yes 112 pop3_uidl_format = %08Xv%08Xu 113 114 auth_mechanisms = plain login 115 116 service auth { 117 user = root 118 } 119 '' 120 121 ( 122 optionalString cfg.enablePAM '' 123 userdb { 124 driver = passwd 125 } 126 127 passdb { 128 driver = pam 129 args = ${optionalString cfg.showPAMFailure "failure_show_msg=yes"} dovecot2 130 } 131 '' 132 ) 133 134 ( 135 optionalString (cfg.mailboxes != {}) '' 136 namespace inbox { 137 inbox=yes 138 ${concatStringsSep "\n" (map mailboxConfig (attrValues cfg.mailboxes))} 139 } 140 '' 141 ) 142 143 ( 144 optionalString cfg.enableQuota '' 145 service quota-status { 146 executable = ${dovecotPkg}/libexec/dovecot/quota-status -p postfix 147 inet_listener { 148 port = ${cfg.quotaPort} 149 } 150 client_limit = 1 151 } 152 153 plugin { 154 quota_rule = *:storage=${cfg.quotaGlobalPerUser} 155 quota = count:User quota # per virtual mail user quota 156 quota_status_success = DUNNO 157 quota_status_nouser = DUNNO 158 quota_status_overquota = "552 5.2.2 Mailbox is full" 159 quota_grace = 10%% 160 quota_vsizes = yes 161 } 162 '' 163 ) 164 165 # General plugin settings: 166 # - sieve is mostly generated here, refer to `pluginSettings` to follow 167 # the control flow. 168 '' 169 plugin { 170 ${concatStringsSep "\n" (mapAttrsToList (key: value: " ${key} = ${value}") cfg.pluginSettings)} 171 } 172 '' 173 174 cfg.extraConfig 175 ]; 176 177 modulesDir = pkgs.symlinkJoin { 178 name = "dovecot-modules"; 179 paths = map (pkg: "${pkg}/lib/dovecot") ([ dovecotPkg ] ++ map (module: module.override { dovecot = dovecotPkg; }) cfg.modules); 180 }; 181 182 mailboxConfig = mailbox: '' 183 mailbox "${mailbox.name}" { 184 auto = ${toString mailbox.auto} 185 '' + optionalString (mailbox.autoexpunge != null) '' 186 autoexpunge = ${mailbox.autoexpunge} 187 '' + optionalString (mailbox.specialUse != null) '' 188 special_use = \${toString mailbox.specialUse} 189 '' + "}"; 190 191 mailboxes = { name, ... }: { 192 options = { 193 name = mkOption { 194 type = types.strMatching ''[^"]+''; 195 example = "Spam"; 196 default = name; 197 readOnly = true; 198 description = "The name of the mailbox."; 199 }; 200 auto = mkOption { 201 type = types.enum [ "no" "create" "subscribe" ]; 202 default = "no"; 203 example = "subscribe"; 204 description = "Whether to automatically create or create and subscribe to the mailbox or not."; 205 }; 206 specialUse = mkOption { 207 type = types.nullOr (types.enum [ "All" "Archive" "Drafts" "Flagged" "Junk" "Sent" "Trash" ]); 208 default = null; 209 example = "Junk"; 210 description = "Null if no special use flag is set. Other than that every use flag mentioned in the RFC is valid."; 211 }; 212 autoexpunge = mkOption { 213 type = types.nullOr types.str; 214 default = null; 215 example = "60d"; 216 description = '' 217 To automatically remove all email from the mailbox which is older than the 218 specified time. 219 ''; 220 }; 221 }; 222 }; 223in 224{ 225 imports = [ 226 (mkRemovedOptionModule [ "services" "dovecot2" "package" ] "") 227 (mkRenamedOptionModule [ "services" "dovecot2" "sieveScripts" ] [ "services" "dovecot2" "sieve" "scripts" ]) 228 ]; 229 230 options.services.dovecot2 = { 231 enable = mkEnableOption "the dovecot 2.x POP3/IMAP server"; 232 233 enablePop3 = mkEnableOption "starting the POP3 listener (when Dovecot is enabled)"; 234 235 enableImap = mkEnableOption "starting the IMAP listener (when Dovecot is enabled)" // { default = true; }; 236 237 enableLmtp = mkEnableOption "starting the LMTP listener (when Dovecot is enabled)"; 238 239 protocols = mkOption { 240 type = types.listOf types.str; 241 default = []; 242 description = "Additional listeners to start when Dovecot is enabled."; 243 }; 244 245 user = mkOption { 246 type = types.str; 247 default = "dovecot2"; 248 description = "Dovecot user name."; 249 }; 250 251 group = mkOption { 252 type = types.str; 253 default = "dovecot2"; 254 description = "Dovecot group name."; 255 }; 256 257 extraConfig = mkOption { 258 type = types.lines; 259 default = ""; 260 example = "mail_debug = yes"; 261 description = "Additional entries to put verbatim into Dovecot's config file."; 262 }; 263 264 mailPlugins = 265 let 266 plugins = hint: types.submodule { 267 options = { 268 enable = mkOption { 269 type = types.listOf types.str; 270 default = []; 271 description = "mail plugins to enable as a list of strings to append to the ${hint} `$mail_plugins` configuration variable"; 272 }; 273 }; 274 }; 275 in 276 mkOption { 277 type = with types; submodule { 278 options = { 279 globally = mkOption { 280 description = "Additional entries to add to the mail_plugins variable for all protocols"; 281 type = plugins "top-level"; 282 example = { enable = [ "virtual" ]; }; 283 default = { enable = []; }; 284 }; 285 perProtocol = mkOption { 286 description = "Additional entries to add to the mail_plugins variable, per protocol"; 287 type = attrsOf (plugins "corresponding per-protocol"); 288 default = {}; 289 example = { imap = [ "imap_acl" ]; }; 290 }; 291 }; 292 }; 293 description = "Additional entries to add to the mail_plugins variable, globally and per protocol"; 294 example = { 295 globally.enable = [ "acl" ]; 296 perProtocol.imap.enable = [ "imap_acl" ]; 297 }; 298 default = { globally.enable = []; perProtocol = {}; }; 299 }; 300 301 configFile = mkOption { 302 type = types.nullOr types.path; 303 default = null; 304 description = "Config file used for the whole dovecot configuration."; 305 apply = v: if v != null then v else pkgs.writeText "dovecot.conf" dovecotConf; 306 }; 307 308 mailLocation = mkOption { 309 type = types.str; 310 default = "maildir:/var/spool/mail/%u"; /* Same as inbox, as postfix */ 311 example = "maildir:~/mail:INBOX=/var/spool/mail/%u"; 312 description = '' 313 Location that dovecot will use for mail folders. Dovecot mail_location option. 314 ''; 315 }; 316 317 mailUser = mkOption { 318 type = types.nullOr types.str; 319 default = null; 320 description = "Default user to store mail for virtual users."; 321 }; 322 323 mailGroup = mkOption { 324 type = types.nullOr types.str; 325 default = null; 326 description = "Default group to store mail for virtual users."; 327 }; 328 329 createMailUser = mkEnableOption ''automatically creating the user 330 given in {option}`services.dovecot.user` and the group 331 given in {option}`services.dovecot.group`.'' // { default = true; }; 332 333 modules = mkOption { 334 type = types.listOf types.package; 335 default = []; 336 example = literalExpression "[ pkgs.dovecot_pigeonhole ]"; 337 description = '' 338 Symlinks the contents of lib/dovecot of every given package into 339 /etc/dovecot/modules. This will make the given modules available 340 if a dovecot package with the module_dir patch applied is being used. 341 ''; 342 }; 343 344 sslCACert = mkOption { 345 type = types.nullOr types.str; 346 default = null; 347 description = "Path to the server's CA certificate key."; 348 }; 349 350 sslServerCert = mkOption { 351 type = types.nullOr types.str; 352 default = null; 353 description = "Path to the server's public key."; 354 }; 355 356 sslServerKey = mkOption { 357 type = types.nullOr types.str; 358 default = null; 359 description = "Path to the server's private key."; 360 }; 361 362 enablePAM = mkEnableOption "creating a own Dovecot PAM service and configure PAM user logins" // { default = true; }; 363 364 enableDHE = mkEnableOption "ssl_dh and generation of primes for the key exchange" // { default = true; }; 365 366 showPAMFailure = mkEnableOption "showing the PAM failure message on authentication error (useful for OTPW)"; 367 368 mailboxes = mkOption { 369 type = with types; coercedTo 370 (listOf unspecified) 371 (list: listToAttrs (map (entry: { name = entry.name; value = removeAttrs entry ["name"]; }) list)) 372 (attrsOf (submodule mailboxes)); 373 default = {}; 374 example = literalExpression '' 375 { 376 Spam = { specialUse = "Junk"; auto = "create"; }; 377 } 378 ''; 379 description = "Configure mailboxes and auto create or subscribe them."; 380 }; 381 382 enableQuota = mkEnableOption "the dovecot quota service"; 383 384 quotaPort = mkOption { 385 type = types.str; 386 default = "12340"; 387 description = '' 388 The Port the dovecot quota service binds to. 389 If using postfix, add check_policy_service inet:localhost:12340 to your smtpd_recipient_restrictions in your postfix config. 390 ''; 391 }; 392 quotaGlobalPerUser = mkOption { 393 type = types.str; 394 default = "100G"; 395 example = "10G"; 396 description = "Quota limit for the user in bytes. Supports suffixes b, k, M, G, T and %."; 397 }; 398 399 400 pluginSettings = mkOption { 401 # types.str does not coerce from packages, like `sievePipeBinScriptDirectory`. 402 type = types.attrsOf (types.oneOf [ types.str types.package ]); 403 default = {}; 404 example = literalExpression '' 405 { 406 sieve = "file:~/sieve;active=~/.dovecot.sieve"; 407 } 408 ''; 409 description = '' 410 Plugin settings for dovecot in general, e.g. `sieve`, `sieve_default`, etc. 411 412 Some of the other knobs of this module will influence by default the plugin settings, but you 413 can still override any plugin settings. 414 415 If you override a plugin setting, its value is cleared and you have to copy over the defaults. 416 ''; 417 }; 418 419 imapsieve.mailbox = mkOption { 420 default = []; 421 description = "Configure Sieve filtering rules on IMAP actions"; 422 type = types.listOf (types.submodule ({ config, ... }: { 423 options = { 424 name = mkOption { 425 description = '' 426 This setting configures the name of a mailbox for which administrator scripts are configured. 427 428 The settings defined hereafter with matching sequence numbers apply to the mailbox named by this setting. 429 430 This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes. 431 ''; 432 example = "Junk"; 433 type = types.str; 434 }; 435 436 from = mkOption { 437 default = null; 438 description = '' 439 Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when the message originates from the indicated mailbox. 440 441 This setting supports wildcards with a syntax compatible with the IMAP LIST command, meaning that this setting can apply to multiple or even all ("*") mailboxes. 442 ''; 443 example = "*"; 444 type = types.nullOr types.str; 445 }; 446 447 causes = mkOption { 448 default = [ ]; 449 description = '' 450 Only execute the administrator Sieve scripts for the mailbox configured with services.dovecot2.imapsieve.mailbox.<name>.name when one of the listed IMAPSIEVE causes apply. 451 452 This has no effect on the user script, which is always executed no matter the cause. 453 ''; 454 example = [ "COPY" "APPEND" ]; 455 type = types.listOf (types.enum [ "APPEND" "COPY" "FLAG" ]); 456 }; 457 458 before = mkOption { 459 default = null; 460 description = '' 461 When an IMAP event of interest occurs, this sieve script is executed before any user script respectively. 462 463 This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_before: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed. 464 ''; 465 example = literalExpression "./report-spam.sieve"; 466 type = types.nullOr types.path; 467 }; 468 469 after = mkOption { 470 default = null; 471 description = '' 472 When an IMAP event of interest occurs, this sieve script is executed after any user script respectively. 473 474 This setting each specify the location of a single sieve script. The semantics of this setting is similar to sieve_after: the specified scripts form a sequence together with the user script in which the next script is only executed when an (implicit) keep action is executed. 475 ''; 476 example = literalExpression "./report-spam.sieve"; 477 type = types.nullOr types.path; 478 }; 479 }; 480 })); 481 }; 482 483 sieve = { 484 plugins = mkOption { 485 default = []; 486 example = [ "sieve_extprograms" ]; 487 description = "Sieve plugins to load"; 488 type = types.listOf types.str; 489 }; 490 491 extensions = mkOption { 492 default = []; 493 description = "Sieve extensions for use in user scripts"; 494 example = [ "notify" "imapflags" "vnd.dovecot.filter" ]; 495 type = types.listOf types.str; 496 }; 497 498 globalExtensions = mkOption { 499 default = []; 500 example = [ "vnd.dovecot.environment" ]; 501 description = "Sieve extensions for use in global scripts"; 502 type = types.listOf types.str; 503 }; 504 505 scripts = mkOption { 506 type = types.attrsOf types.path; 507 default = {}; 508 description = "Sieve scripts to be executed. Key is a sequence, e.g. 'before2', 'after' etc."; 509 }; 510 511 pipeBins = mkOption { 512 default = []; 513 example = literalExpression '' 514 map lib.getExe [ 515 (pkgs.writeShellScriptBin "learn-ham.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_ham") 516 (pkgs.writeShellScriptBin "learn-spam.sh" "exec ''${pkgs.rspamd}/bin/rspamc learn_spam") 517 ] 518 ''; 519 description = "Programs available for use by the vnd.dovecot.pipe extension"; 520 type = types.listOf types.path; 521 }; 522 }; 523 }; 524 525 config = mkIf cfg.enable { 526 security.pam.services.dovecot2 = mkIf cfg.enablePAM {}; 527 528 security.dhparams = mkIf (cfg.sslServerCert != null && cfg.enableDHE) { 529 enable = true; 530 params.dovecot2 = {}; 531 }; 532 533 services.dovecot2 = { 534 protocols = 535 optional cfg.enableImap "imap" 536 ++ optional cfg.enablePop3 "pop3" 537 ++ optional cfg.enableLmtp "lmtp"; 538 539 mailPlugins = mkIf cfg.enableQuota { 540 globally.enable = [ "quota" ]; 541 perProtocol.imap.enable = [ "imap_quota" ]; 542 }; 543 544 sieve.plugins = 545 optional (cfg.imapsieve.mailbox != []) "sieve_imapsieve" 546 ++ optional (cfg.sieve.pipeBins != []) "sieve_extprograms"; 547 548 sieve.globalExtensions = optional (cfg.sieve.pipeBins != []) "vnd.dovecot.pipe"; 549 550 pluginSettings = lib.mapAttrs (n: lib.mkDefault) ({ 551 sieve_plugins = concatStringsSep " " cfg.sieve.plugins; 552 sieve_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.extensions); 553 sieve_global_extensions = concatStringsSep " " (map (el: "+${el}") cfg.sieve.globalExtensions); 554 sieve_pipe_bin_dir = sievePipeBinScriptDirectory; 555 } // sieveScriptSettings // imapSieveMailboxSettings); 556 }; 557 558 users.users = { 559 dovenull = 560 { 561 uid = config.ids.uids.dovenull2; 562 description = "Dovecot user for untrusted logins"; 563 group = "dovenull"; 564 }; 565 } // optionalAttrs (cfg.user == "dovecot2") { 566 dovecot2 = 567 { 568 uid = config.ids.uids.dovecot2; 569 description = "Dovecot user"; 570 group = cfg.group; 571 }; 572 } // optionalAttrs (cfg.createMailUser && cfg.mailUser != null) { 573 ${cfg.mailUser} = 574 { description = "Virtual Mail User"; isSystemUser = true; } // optionalAttrs (cfg.mailGroup != null) 575 { group = cfg.mailGroup; }; 576 }; 577 578 users.groups = { 579 dovenull.gid = config.ids.gids.dovenull2; 580 } // optionalAttrs (cfg.group == "dovecot2") { 581 dovecot2.gid = config.ids.gids.dovecot2; 582 } // optionalAttrs (cfg.createMailUser && cfg.mailGroup != null) { 583 ${cfg.mailGroup} = {}; 584 }; 585 586 environment.etc."dovecot/modules".source = modulesDir; 587 environment.etc."dovecot/dovecot.conf".source = cfg.configFile; 588 589 systemd.services.dovecot2 = { 590 description = "Dovecot IMAP/POP3 server"; 591 592 after = [ "network.target" ]; 593 wantedBy = [ "multi-user.target" ]; 594 restartTriggers = [ cfg.configFile modulesDir ]; 595 596 startLimitIntervalSec = 60; # 1 min 597 serviceConfig = { 598 Type = "notify"; 599 ExecStart = "${dovecotPkg}/sbin/dovecot -F"; 600 ExecReload = "${dovecotPkg}/sbin/doveadm reload"; 601 Restart = "on-failure"; 602 RestartSec = "1s"; 603 RuntimeDirectory = [ "dovecot2" ]; 604 }; 605 606 # When copying sieve scripts preserve the original time stamp 607 # (should be 0) so that the compiled sieve script is newer than 608 # the source file and Dovecot won't try to compile it. 609 preStart = '' 610 rm -rf ${stateDir}/sieve ${stateDir}/imapsieve 611 '' + optionalString (cfg.sieve.scripts != {}) '' 612 mkdir -p ${stateDir}/sieve 613 ${concatStringsSep "\n" ( 614 mapAttrsToList ( 615 to: from: '' 616 if [ -d '${from}' ]; then 617 mkdir '${stateDir}/sieve/${to}' 618 cp -p "${from}/"*.sieve '${stateDir}/sieve/${to}' 619 else 620 cp -p '${from}' '${stateDir}/sieve/${to}' 621 fi 622 ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/sieve/${to}' 623 '' 624 ) cfg.sieve.scripts 625 )} 626 chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/sieve' 627 '' 628 + optionalString (cfg.imapsieve.mailbox != []) '' 629 mkdir -p ${stateDir}/imapsieve/{before,after} 630 631 ${ 632 concatMapStringsSep "\n" 633 (el: 634 optionalString (el.before != null) '' 635 cp -p ${el.before} ${stateDir}/imapsieve/before/${baseNameOf el.before} 636 ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/before/${baseNameOf el.before}' 637 '' 638 + optionalString (el.after != null) '' 639 cp -p ${el.after} ${stateDir}/imapsieve/after/${baseNameOf el.after} 640 ${pkgs.dovecot_pigeonhole}/bin/sievec '${stateDir}/imapsieve/after/${baseNameOf el.after}' 641 '' 642 ) 643 cfg.imapsieve.mailbox 644 } 645 646 ${ 647 optionalString (cfg.mailUser != null && cfg.mailGroup != null) 648 "chown -R '${cfg.mailUser}:${cfg.mailGroup}' '${stateDir}/imapsieve'" 649 } 650 ''; 651 }; 652 653 environment.systemPackages = [ dovecotPkg ]; 654 655 warnings = warnAboutExtraConfigCollisions; 656 657 assertions = [ 658 { 659 assertion = (cfg.sslServerCert == null) == (cfg.sslServerKey == null) 660 && (cfg.sslCACert != null -> !(cfg.sslServerCert == null || cfg.sslServerKey == null)); 661 message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto"; 662 } 663 { 664 assertion = cfg.showPAMFailure -> cfg.enablePAM; 665 message = "dovecot is configured with showPAMFailure while enablePAM is disabled"; 666 } 667 { 668 assertion = cfg.sieve.scripts != {} -> (cfg.mailUser != null && cfg.mailGroup != null); 669 message = "dovecot requires mailUser and mailGroup to be set when `sieve.scripts` is set"; 670 } 671 ]; 672 673 }; 674 675 meta.maintainers = [ lib.maintainers.dblsaiko ]; 676}