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