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}