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}