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