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