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