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