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