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