at 17.09-beta 26 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 7 cfg = config.services.postfix; 8 user = cfg.user; 9 group = cfg.group; 10 setgidGroup = cfg.setgidGroup; 11 12 haveAliases = cfg.postmasterAlias != "" || cfg.rootAlias != "" 13 || cfg.extraAliases != ""; 14 haveTransport = cfg.transport != ""; 15 haveVirtual = cfg.virtual != ""; 16 17 clientAccess = 18 if (cfg.dnsBlacklistOverrides != "") 19 then [ "check_client_access hash:/etc/postfix/client_access" ] 20 else []; 21 22 dnsBl = 23 if (cfg.dnsBlacklists != []) 24 then [ (concatStringsSep ", " (map (s: "reject_rbl_client " + s) cfg.dnsBlacklists)) ] 25 else []; 26 27 clientRestrictions = concatStringsSep ", " (clientAccess ++ dnsBl); 28 29 mainCf = let 30 escape = replaceStrings ["$"] ["$$"]; 31 mkList = items: "\n " + concatMapStringsSep "\n " escape items; 32 mkVal = value: 33 if isList value then mkList value 34 else " " + (if value == true then "yes" 35 else if value == false then "no" 36 else toString value); 37 mkEntry = name: value: "${escape name} =${mkVal value}"; 38 in 39 concatStringsSep "\n" (mapAttrsToList mkEntry (recursiveUpdate defaultConf cfg.config)) 40 + "\n" + cfg.extraConfig; 41 42 defaultConf = { 43 compatibility_level = "9999"; 44 mail_owner = user; 45 default_privs = "nobody"; 46 47 # NixOS specific locations 48 data_directory = "/var/lib/postfix/data"; 49 queue_directory = "/var/lib/postfix/queue"; 50 51 # Default location of everything in package 52 meta_directory = "${pkgs.postfix}/etc/postfix"; 53 command_directory = "${pkgs.postfix}/bin"; 54 sample_directory = "/etc/postfix"; 55 newaliases_path = "${pkgs.postfix}/bin/newaliases"; 56 mailq_path = "${pkgs.postfix}/bin/mailq"; 57 readme_directory = false; 58 sendmail_path = "${pkgs.postfix}/bin/sendmail"; 59 daemon_directory = "${pkgs.postfix}/libexec/postfix"; 60 manpage_directory = "${pkgs.postfix}/share/man"; 61 html_directory = "${pkgs.postfix}/share/postfix/doc/html"; 62 shlib_directory = false; 63 relayhost = if cfg.lookupMX || cfg.relayHost == "" 64 then cfg.relayHost 65 else "[${cfg.relayHost}]"; 66 mail_spool_directory = "/var/spool/mail/"; 67 setgid_group = setgidGroup; 68 } 69 // optionalAttrs config.networking.enableIPv6 { inet_protocols = "all"; } 70 // optionalAttrs (cfg.networks != null) { mynetworks = cfg.networks; } 71 // optionalAttrs (cfg.networksStyle != "") { mynetworks_style = cfg.networksStyle; } 72 // optionalAttrs (cfg.hostname != "") { myhostname = cfg.hostname; } 73 // optionalAttrs (cfg.domain != "") { mydomain = cfg.domain; } 74 // optionalAttrs (cfg.origin != "") { myorigin = cfg.origin; } 75 // optionalAttrs (cfg.destination != null) { mydestination = cfg.destination; } 76 // optionalAttrs (cfg.relayDomains != null) { relay_domains = cfg.relayDomains; } 77 // optionalAttrs (cfg.recipientDelimiter != "") { recipient_delimiter = cfg.recipientDelimiter; } 78 // optionalAttrs haveAliases { alias_maps = "${cfg.aliasMapType}:/etc/postfix/aliases"; } 79 // optionalAttrs haveTransport { transport_maps = "hash:/etc/postfix/transport"; } 80 // optionalAttrs haveVirtual { virtual_alias_maps = "${cfg.virtualMapType}:/etc/postfix/virtual"; } 81 // optionalAttrs (cfg.dnsBlacklists != []) { smtpd_client_restrictions = clientRestrictions; } 82 // optionalAttrs cfg.enableHeaderChecks { header_checks = "regexp:/etc/postfix/header_checks"; } 83 // optionalAttrs (cfg.sslCert != "") { 84 smtp_tls_CAfile = cfg.sslCACert; 85 smtp_tls_cert_file = cfg.sslCert; 86 smtp_tls_key_file = cfg.sslKey; 87 88 smtp_use_tls = true; 89 90 smtpd_tls_CAfile = cfg.sslCACert; 91 smtpd_tls_cert_file = cfg.sslCert; 92 smtpd_tls_key_file = cfg.sslKey; 93 94 smtpd_use_tls = true; 95 }; 96 97 masterCfOptions = { options, config, name, ... }: { 98 options = { 99 name = mkOption { 100 type = types.str; 101 default = name; 102 example = "smtp"; 103 description = '' 104 The name of the service to run. Defaults to the attribute set key. 105 ''; 106 }; 107 108 type = mkOption { 109 type = types.enum [ "inet" "unix" "fifo" "pass" ]; 110 default = "unix"; 111 example = "inet"; 112 description = "The type of the service"; 113 }; 114 115 private = mkOption { 116 type = types.bool; 117 example = false; 118 description = '' 119 Whether the service's sockets and storage directory is restricted to 120 be only available via the mail system. If <literal>null</literal> is 121 given it uses the postfix default <literal>true</literal>. 122 ''; 123 }; 124 125 privileged = mkOption { 126 type = types.bool; 127 example = true; 128 description = ""; 129 }; 130 131 chroot = mkOption { 132 type = types.bool; 133 example = true; 134 description = '' 135 Whether the service is chrooted to have only access to the 136 <option>services.postfix.queueDir</option> and the closure of 137 store paths specified by the <option>program</option> option. 138 ''; 139 }; 140 141 wakeup = mkOption { 142 type = types.int; 143 example = 60; 144 description = '' 145 Automatically wake up the service after the specified number of 146 seconds. If <literal>0</literal> is given, never wake the service 147 up. 148 ''; 149 }; 150 151 wakeupUnusedComponent = mkOption { 152 type = types.bool; 153 example = false; 154 description = '' 155 If set to <literal>false</literal> the component will only be woken 156 up if it is used. This is equivalent to postfix' notion of adding a 157 question mark behind the wakeup time in 158 <filename>master.cf</filename> 159 ''; 160 }; 161 162 maxproc = mkOption { 163 type = types.int; 164 example = 1; 165 description = '' 166 The maximum number of processes to spawn for this service. If the 167 value is <literal>0</literal> it doesn't have any limit. If 168 <literal>null</literal> is given it uses the postfix default of 169 <literal>100</literal>. 170 ''; 171 }; 172 173 command = mkOption { 174 type = types.str; 175 default = name; 176 example = "smtpd"; 177 description = '' 178 A program name specifying a Postfix service/daemon process. 179 By default it's the attribute <option>name</option>. 180 ''; 181 }; 182 183 args = mkOption { 184 type = types.listOf types.str; 185 default = []; 186 example = [ "-o" "smtp_helo_timeout=5" ]; 187 description = '' 188 Arguments to pass to the <option>command</option>. There is no shell 189 processing involved and shell syntax is passed verbatim to the 190 process. 191 ''; 192 }; 193 194 rawEntry = mkOption { 195 type = types.listOf types.str; 196 default = []; 197 internal = true; 198 description = '' 199 The raw configuration line for the <filename>master.cf</filename>. 200 ''; 201 }; 202 }; 203 204 config.rawEntry = let 205 mkBool = bool: if bool then "y" else "n"; 206 mkArg = arg: "${optionalString (hasPrefix "-" arg) "\n "}${arg}"; 207 208 maybeOption = fun: option: 209 if options.${option}.isDefined then fun config.${option} else "-"; 210 211 # This is special, because we have two options for this value. 212 wakeup = let 213 wakeupDefined = options.wakeup.isDefined; 214 wakeupUCDefined = options.wakeupUnusedComponent.isDefined; 215 finalValue = toString config.wakeup 216 + optionalString (wakeupUCDefined && !config.wakeupUnusedComponent) "?"; 217 in if wakeupDefined then finalValue else "-"; 218 219 in [ 220 config.name 221 config.type 222 (maybeOption mkBool "private") 223 (maybeOption (b: mkBool (!b)) "privileged") 224 (maybeOption mkBool "chroot") 225 wakeup 226 (maybeOption toString "maxproc") 227 (config.command + " " + concatMapStringsSep " " mkArg config.args) 228 ]; 229 }; 230 231 masterCfContent = let 232 233 labels = [ 234 "# service" "type" "private" "unpriv" "chroot" "wakeup" "maxproc" 235 "command + args" 236 ]; 237 238 labelDefaults = [ 239 "# " "" "(yes)" "(yes)" "(no)" "(never)" "(100)" "" "" 240 ]; 241 242 masterCf = mapAttrsToList (const (getAttr "rawEntry")) cfg.masterConfig; 243 244 # A list of the maximum width of the columns across all lines and labels 245 maxWidths = let 246 foldLine = line: acc: let 247 columnLengths = map stringLength line; 248 in zipListsWith max acc columnLengths; 249 # We need to handle the last column specially here, because it's 250 # open-ended (command + args). 251 lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf); 252 in fold foldLine (genList (const 0) (length labels)) lines; 253 254 # Pad a string with spaces from the right (opposite of fixedWidthString). 255 pad = width: str: let 256 padWidth = width - stringLength str; 257 padding = concatStrings (genList (const " ") padWidth); 258 in str + optionalString (padWidth > 0) padding; 259 260 # It's + 2 here, because that's the amount of spacing between columns. 261 fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths; 262 263 formatLine = line: concatStringsSep " " (zipListsWith pad maxWidths line); 264 265 formattedLabels = let 266 sep = "# " + concatStrings (genList (const "=") (fullWidth + 5)); 267 lines = [ sep (formatLine labels) (formatLine labelDefaults) sep ]; 268 in concatStringsSep "\n" lines; 269 270 in formattedLabels + "\n" + concatMapStringsSep "\n" formatLine masterCf + "\n" + cfg.extraMasterConf; 271 272 headerCheckOptions = { ... }: 273 { 274 options = { 275 pattern = mkOption { 276 type = types.str; 277 default = "/^.*/"; 278 example = "/^X-Mailer:/"; 279 description = "A regexp pattern matching the header"; 280 }; 281 action = mkOption { 282 type = types.str; 283 default = "DUNNO"; 284 example = "BCC mail@example.com"; 285 description = "The action to be executed when the pattern is matched"; 286 }; 287 }; 288 }; 289 290 headerChecks = concatStringsSep "\n" (map (x: "${x.pattern} ${x.action}") cfg.headerChecks) + cfg.extraHeaderChecks; 291 292 aliases = let seperator = if cfg.aliasMapType == "hash" then ":" else ""; in 293 optionalString (cfg.postmasterAlias != "") '' 294 postmaster${seperator} ${cfg.postmasterAlias} 295 '' 296 + optionalString (cfg.rootAlias != "") '' 297 root${seperator} ${cfg.rootAlias} 298 '' 299 + cfg.extraAliases 300 ; 301 302 aliasesFile = pkgs.writeText "postfix-aliases" aliases; 303 virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual; 304 checkClientAccessFile = pkgs.writeText "postfix-check-client-access" cfg.dnsBlacklistOverrides; 305 mainCfFile = pkgs.writeText "postfix-main.cf" mainCf; 306 masterCfFile = pkgs.writeText "postfix-master.cf" masterCfContent; 307 transportFile = pkgs.writeText "postfix-transport" cfg.transport; 308 headerChecksFile = pkgs.writeText "postfix-header-checks" headerChecks; 309 310in 311 312{ 313 314 ###### interface 315 316 options = { 317 318 services.postfix = { 319 320 enable = mkOption { 321 type = types.bool; 322 default = false; 323 description = "Whether to run the Postfix mail server."; 324 }; 325 326 enableSmtp = mkOption { 327 default = true; 328 description = "Whether to enable smtp in master.cf."; 329 }; 330 331 enableSubmission = mkOption { 332 type = types.bool; 333 default = false; 334 description = "Whether to enable smtp submission."; 335 }; 336 337 submissionOptions = mkOption { 338 type = types.attrs; 339 default = { 340 smtpd_tls_security_level = "encrypt"; 341 smtpd_sasl_auth_enable = "yes"; 342 smtpd_client_restrictions = "permit_sasl_authenticated,reject"; 343 milter_macro_daemon_name = "ORIGINATING"; 344 }; 345 example = { 346 smtpd_tls_security_level = "encrypt"; 347 smtpd_sasl_auth_enable = "yes"; 348 smtpd_sasl_type = "dovecot"; 349 smtpd_client_restrictions = "permit_sasl_authenticated,reject"; 350 milter_macro_daemon_name = "ORIGINATING"; 351 }; 352 description = "Options for the submission config in master.cf"; 353 }; 354 355 setSendmail = mkOption { 356 type = types.bool; 357 default = true; 358 description = "Whether to set the system sendmail to postfix's."; 359 }; 360 361 user = mkOption { 362 type = types.str; 363 default = "postfix"; 364 description = "What to call the Postfix user (must be used only for postfix)."; 365 }; 366 367 group = mkOption { 368 type = types.str; 369 default = "postfix"; 370 description = "What to call the Postfix group (must be used only for postfix)."; 371 }; 372 373 setgidGroup = mkOption { 374 type = types.str; 375 default = "postdrop"; 376 description = " 377 How to call postfix setgid group (for postdrop). Should 378 be uniquely used group. 379 "; 380 }; 381 382 networks = mkOption { 383 type = types.nullOr (types.listOf types.str); 384 default = null; 385 example = ["192.168.0.1/24"]; 386 description = " 387 Net masks for trusted - allowed to relay mail to third parties - 388 hosts. Leave empty to use mynetworks_style configuration or use 389 default (localhost-only). 390 "; 391 }; 392 393 networksStyle = mkOption { 394 type = types.str; 395 default = ""; 396 description = " 397 Name of standard way of trusted network specification to use, 398 leave blank if you specify it explicitly or if you want to use 399 default (localhost-only). 400 "; 401 }; 402 403 hostname = mkOption { 404 type = types.str; 405 default = ""; 406 description =" 407 Hostname to use. Leave blank to use just the hostname of machine. 408 It should be FQDN. 409 "; 410 }; 411 412 domain = mkOption { 413 type = types.str; 414 default = ""; 415 description =" 416 Domain to use. Leave blank to use hostname minus first component. 417 "; 418 }; 419 420 origin = mkOption { 421 type = types.str; 422 default = ""; 423 description =" 424 Origin to use in outgoing e-mail. Leave blank to use hostname. 425 "; 426 }; 427 428 destination = mkOption { 429 type = types.nullOr (types.listOf types.str); 430 default = null; 431 example = ["localhost"]; 432 description = " 433 Full (!) list of domains we deliver locally. Leave blank for 434 acceptable Postfix default. 435 "; 436 }; 437 438 relayDomains = mkOption { 439 type = types.nullOr (types.listOf types.str); 440 default = null; 441 example = ["localdomain"]; 442 description = " 443 List of domains we agree to relay to. Default is empty. 444 "; 445 }; 446 447 relayHost = mkOption { 448 type = types.str; 449 default = ""; 450 description = " 451 Mail relay for outbound mail. 452 "; 453 }; 454 455 lookupMX = mkOption { 456 type = types.bool; 457 default = false; 458 description = " 459 Whether relay specified is just domain whose MX must be used. 460 "; 461 }; 462 463 postmasterAlias = mkOption { 464 type = types.str; 465 default = "root"; 466 description = "Who should receive postmaster e-mail."; 467 }; 468 469 rootAlias = mkOption { 470 type = types.str; 471 default = ""; 472 description = " 473 Who should receive root e-mail. Blank for no redirection. 474 "; 475 }; 476 477 extraAliases = mkOption { 478 type = types.lines; 479 default = ""; 480 description = " 481 Additional entries to put verbatim into aliases file, cf. man-page aliases(8). 482 "; 483 }; 484 485 aliasMapType = mkOption { 486 type = with types; enum [ "hash" "regexp" "pcre" ]; 487 default = "hash"; 488 example = "regexp"; 489 description = "The format the alias map should have. Use regexp if you want to use regular expressions."; 490 }; 491 492 config = mkOption { 493 type = with types; attrsOf (either bool (either str (listOf str))); 494 default = defaultConf; 495 description = '' 496 The main.cf configuration file as key value set. 497 ''; 498 example = { 499 mail_owner = "postfix"; 500 smtp_use_tls = true; 501 }; 502 }; 503 504 extraConfig = mkOption { 505 type = types.lines; 506 default = ""; 507 description = " 508 Extra lines to be added verbatim to the main.cf configuration file. 509 "; 510 }; 511 512 sslCert = mkOption { 513 type = types.str; 514 default = ""; 515 description = "SSL certificate to use."; 516 }; 517 518 sslCACert = mkOption { 519 type = types.str; 520 default = ""; 521 description = "SSL certificate of CA."; 522 }; 523 524 sslKey = mkOption { 525 type = types.str; 526 default = ""; 527 description = "SSL key to use."; 528 }; 529 530 recipientDelimiter = mkOption { 531 type = types.str; 532 default = ""; 533 example = "+"; 534 description = " 535 Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test 536 "; 537 }; 538 539 virtual = mkOption { 540 type = types.lines; 541 default = ""; 542 description = " 543 Entries for the virtual alias map, cf. man-page virtual(8). 544 "; 545 }; 546 547 virtualMapType = mkOption { 548 type = types.enum ["hash" "regexp" "pcre"]; 549 default = "hash"; 550 description = '' 551 What type of virtual alias map file to use. Use <literal>"regexp"</literal> for regular expressions. 552 ''; 553 }; 554 555 transport = mkOption { 556 default = ""; 557 description = " 558 Entries for the transport map, cf. man-page transport(8). 559 "; 560 }; 561 562 dnsBlacklists = mkOption { 563 default = []; 564 type = with types; listOf string; 565 description = "dns blacklist servers to use with smtpd_client_restrictions"; 566 }; 567 568 dnsBlacklistOverrides = mkOption { 569 default = ""; 570 description = "contents of check_client_access for overriding dnsBlacklists"; 571 }; 572 573 masterConfig = mkOption { 574 type = types.attrsOf (types.submodule masterCfOptions); 575 default = {}; 576 example = 577 { submission = { 578 type = "inet"; 579 args = [ "-o" "smtpd_tls_security_level=encrypt" ]; 580 }; 581 }; 582 description = '' 583 An attribute set of service options, which correspond to the service 584 definitions usually done within the Postfix 585 <filename>master.cf</filename> file. 586 ''; 587 }; 588 589 extraMasterConf = mkOption { 590 type = types.lines; 591 default = ""; 592 example = "submission inet n - n - - smtpd"; 593 description = "Extra lines to append to the generated master.cf file."; 594 }; 595 596 enableHeaderChecks = mkOption { 597 type = types.bool; 598 default = false; 599 example = true; 600 description = "Whether to enable postfix header checks"; 601 }; 602 603 headerChecks = mkOption { 604 type = types.listOf (types.submodule headerCheckOptions); 605 default = []; 606 example = [ { pattern = "/^X-Spam-Flag:/"; action = "REDIRECT spam@example.com"; } ]; 607 description = "Postfix header checks."; 608 }; 609 610 extraHeaderChecks = mkOption { 611 type = types.lines; 612 default = ""; 613 example = "/^X-Spam-Flag:/ REDIRECT spam@example.com"; 614 description = "Extra lines to /etc/postfix/header_checks file."; 615 }; 616 617 aliasFiles = mkOption { 618 type = types.attrsOf types.path; 619 default = {}; 620 description = "Aliases' tables to be compiled and placed into /var/lib/postfix/conf."; 621 }; 622 623 mapFiles = mkOption { 624 type = types.attrsOf types.path; 625 default = {}; 626 description = "Maps to be compiled and placed into /var/lib/postfix/conf."; 627 }; 628 629 }; 630 631 }; 632 633 634 ###### implementation 635 636 config = mkIf config.services.postfix.enable (mkMerge [ 637 { 638 639 environment = { 640 etc = singleton 641 { source = "/var/lib/postfix/conf"; 642 target = "postfix"; 643 }; 644 645 # This makes comfortable for root to run 'postqueue' for example. 646 systemPackages = [ pkgs.postfix ]; 647 }; 648 649 services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail { 650 program = "sendmail"; 651 source = "${pkgs.postfix}/bin/sendmail"; 652 group = setgidGroup; 653 setuid = false; 654 setgid = true; 655 }; 656 657 users.extraUsers = optional (user == "postfix") 658 { name = "postfix"; 659 description = "Postfix mail server user"; 660 uid = config.ids.uids.postfix; 661 group = group; 662 }; 663 664 users.extraGroups = 665 optional (group == "postfix") 666 { name = group; 667 gid = config.ids.gids.postfix; 668 } 669 ++ optional (setgidGroup == "postdrop") 670 { name = setgidGroup; 671 gid = config.ids.gids.postdrop; 672 }; 673 674 systemd.services.postfix = 675 { description = "Postfix mail server"; 676 677 wantedBy = [ "multi-user.target" ]; 678 after = [ "network.target" ]; 679 path = [ pkgs.postfix ]; 680 681 serviceConfig = { 682 Type = "forking"; 683 Restart = "always"; 684 PIDFile = "/var/lib/postfix/queue/pid/master.pid"; 685 ExecStart = "${pkgs.postfix}/bin/postfix start"; 686 ExecStop = "${pkgs.postfix}/bin/postfix stop"; 687 ExecReload = "${pkgs.postfix}/bin/postfix reload"; 688 }; 689 690 preStart = '' 691 # Backwards compatibility 692 if [ ! -d /var/lib/postfix ] && [ -d /var/postfix ]; then 693 mkdir -p /var/lib 694 mv /var/postfix /var/lib/postfix 695 fi 696 697 # All permissions set according ${pkgs.postfix}/etc/postfix/postfix-files script 698 mkdir -p /var/lib/postfix /var/lib/postfix/queue/{pid,public,maildrop} 699 chmod 0755 /var/lib/postfix 700 chown root:root /var/lib/postfix 701 702 rm -rf /var/lib/postfix/conf 703 mkdir -p /var/lib/postfix/conf 704 chmod 0755 /var/lib/postfix/conf 705 ln -sf ${pkgs.postfix}/etc/postfix/postfix-files /var/lib/postfix/conf/postfix-files 706 ln -sf ${mainCfFile} /var/lib/postfix/conf/main.cf 707 ln -sf ${masterCfFile} /var/lib/postfix/conf/master.cf 708 709 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 710 ln -sf ${from} /var/lib/postfix/conf/${to} 711 ${pkgs.postfix}/bin/postalias /var/lib/postfix/conf/${to} 712 '') cfg.aliasFiles)} 713 ${concatStringsSep "\n" (mapAttrsToList (to: from: '' 714 ln -sf ${from} /var/lib/postfix/conf/${to} 715 ${pkgs.postfix}/bin/postmap /var/lib/postfix/conf/${to} 716 '') cfg.mapFiles)} 717 718 mkdir -p /var/spool/mail 719 chown root:root /var/spool/mail 720 chmod a+rwxt /var/spool/mail 721 ln -sf /var/spool/mail /var/ 722 723 #Finally delegate to postfix checking remain directories in /var/lib/postfix and set permissions on them 724 ${pkgs.postfix}/bin/postfix set-permissions config_directory=/var/lib/postfix/conf 725 ''; 726 }; 727 728 services.postfix.masterConfig = { 729 smtp_inet = { 730 name = "smtp"; 731 type = "inet"; 732 private = false; 733 command = "smtpd"; 734 }; 735 pickup = { 736 private = false; 737 wakeup = 60; 738 maxproc = 1; 739 }; 740 cleanup = { 741 private = false; 742 maxproc = 0; 743 }; 744 qmgr = { 745 private = false; 746 wakeup = 300; 747 maxproc = 1; 748 }; 749 tlsmgr = { 750 wakeup = 1000; 751 wakeupUnusedComponent = false; 752 maxproc = 1; 753 }; 754 rewrite = { 755 command = "trivial-rewrite"; 756 }; 757 bounce = { 758 maxproc = 0; 759 }; 760 defer = { 761 maxproc = 0; 762 command = "bounce"; 763 }; 764 trace = { 765 maxproc = 0; 766 command = "bounce"; 767 }; 768 verify = { 769 maxproc = 1; 770 }; 771 flush = { 772 private = false; 773 wakeup = 1000; 774 wakeupUnusedComponent = false; 775 maxproc = 0; 776 }; 777 proxymap = { 778 command = "proxymap"; 779 }; 780 proxywrite = { 781 maxproc = 1; 782 command = "proxymap"; 783 }; 784 showq = { 785 private = false; 786 }; 787 error = {}; 788 retry = { 789 command = "error"; 790 }; 791 discard = {}; 792 local = { 793 privileged = true; 794 }; 795 virtual = { 796 privileged = true; 797 }; 798 lmtp = { 799 }; 800 anvil = { 801 maxproc = 1; 802 }; 803 scache = { 804 maxproc = 1; 805 }; 806 } // optionalAttrs cfg.enableSubmission { 807 submission = { 808 type = "inet"; 809 private = false; 810 command = "smtpd"; 811 args = let 812 mkKeyVal = opt: val: [ "-o" (opt + "=" + val) ]; 813 in concatLists (mapAttrsToList mkKeyVal cfg.submissionOptions); 814 }; 815 } // optionalAttrs cfg.enableSmtp { 816 smtp = {}; 817 relay = { 818 command = "smtp"; 819 args = [ "-o" "smtp_fallback_relay=" ]; 820 }; 821 }; 822 } 823 824 (mkIf haveAliases { 825 services.postfix.aliasFiles."aliases" = aliasesFile; 826 }) 827 (mkIf haveTransport { 828 services.postfix.mapFiles."transport" = transportFile; 829 }) 830 (mkIf haveVirtual { 831 services.postfix.mapFiles."virtual" = virtualFile; 832 }) 833 (mkIf cfg.enableHeaderChecks { 834 services.postfix.mapFiles."header_checks" = headerChecksFile; 835 }) 836 (mkIf (cfg.dnsBlacklists != []) { 837 services.postfix.mapFiles."client_access" = checkClientAccessFile; 838 }) 839 (mkIf (cfg.extraConfig != "") { 840 warnings = [ "The services.postfix.extraConfig option was deprecated. Please use services.postfix.config instead." ]; 841 }) 842 (mkIf (cfg.extraMasterConf != "") { 843 warnings = [ "The services.postfix.extraMasterConf option was deprecated. Please use services.postfix.masterConfig instead." ]; 844 }) 845 ]); 846}