1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.postfix;
8 user = cfg.user;
9 group = cfg.group;
10 setgidGroup = cfg.setgidGroup;
11
12 mainCf =
13 ''
14 queue_directory = /var/postfix/queue
15 command_directory = ${pkgs.postfix}/sbin
16 daemon_directory = ${pkgs.postfix}/libexec/postfix
17
18 mail_owner = ${user}
19 default_privs = nobody
20
21 ''
22 + optionalString config.networking.enableIPv6 ''
23 inet_protocols = all
24 ''
25 + (if cfg.networks != null then
26 ''
27 mynetworks = ${concatStringsSep ", " cfg.networks}
28 ''
29 else if cfg.networksStyle != "" then
30 ''
31 mynetworks_style = ${cfg.networksStyle}
32 ''
33 else
34 # Postfix default is subnet, but let's play safe
35 ''
36 mynetworks_style = host
37 '')
38 + optionalString (cfg.hostname != "") ''
39 myhostname = ${cfg.hostname}
40 ''
41 + optionalString (cfg.domain != "") ''
42 mydomain = ${cfg.domain}
43 ''
44 + optionalString (cfg.origin != "") ''
45 myorigin = ${cfg.origin}
46 ''
47 + optionalString (cfg.destination != null) ''
48 mydestination = ${concatStringsSep ", " cfg.destination}
49 ''
50 + optionalString (cfg.relayDomains != null) ''
51 relay_domains = ${concatStringsSep ", " cfg.relayDomains}
52 ''
53 + ''
54 local_recipient_maps =
55
56 relayhost = ${if cfg.lookupMX || cfg.relayHost == "" then
57 cfg.relayHost
58 else
59 "[" + cfg.relayHost + "]"}
60
61 alias_maps = hash:/var/postfix/conf/aliases
62
63 mail_spool_directory = /var/spool/mail/
64
65 setgid_group = ${setgidGroup}
66 ''
67 + optionalString (cfg.sslCert != "") ''
68
69 smtp_tls_CAfile = ${cfg.sslCACert}
70 smtp_tls_cert_file = ${cfg.sslCert}
71 smtp_tls_key_file = ${cfg.sslKey}
72
73 smtp_use_tls = yes
74
75 smtpd_tls_CAfile = ${cfg.sslCACert}
76 smtpd_tls_cert_file = ${cfg.sslCert}
77 smtpd_tls_key_file = ${cfg.sslKey}
78
79 smtpd_use_tls = yes
80
81 recipient_delimiter = ${cfg.recipientDelimiter}
82 ''
83 + optionalString (cfg.virtual != "") ''
84 virtual_alias_maps = hash:/etc/postfix/virtual
85 ''
86 + cfg.extraConfig;
87
88 masterCf = ''
89 # ==========================================================================
90 # service type private unpriv chroot wakeup maxproc command + args
91 # (yes) (yes) (yes) (never) (100)
92 # ==========================================================================
93 smtp inet n - n - - smtpd
94 #submission inet n - n - - smtpd
95 # -o smtpd_tls_security_level=encrypt
96 # -o smtpd_sasl_auth_enable=yes
97 # -o smtpd_client_restrictions=permit_sasl_authenticated,reject
98 # -o milter_macro_daemon_name=ORIGINATING
99 pickup unix n - n 60 1 pickup
100 cleanup unix n - n - 0 cleanup
101 qmgr unix n - n 300 1 qmgr
102 tlsmgr unix - - n 1000? 1 tlsmgr
103 rewrite unix - - n - - trivial-rewrite
104 bounce unix - - n - 0 bounce
105 defer unix - - n - 0 bounce
106 trace unix - - n - 0 bounce
107 verify unix - - n - 1 verify
108 flush unix n - n 1000? 0 flush
109 proxymap unix - - n - - proxymap
110 proxywrite unix - - n - 1 proxymap
111 smtp unix - - n - - smtp
112 relay unix - - n - - smtp
113 -o smtp_fallback_relay=
114 # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
115 showq unix n - n - - showq
116 error unix - - n - - error
117 retry unix - - n - - error
118 discard unix - - n - - discard
119 local unix - n n - - local
120 virtual unix - n n - - virtual
121 lmtp unix - - n - - lmtp
122 anvil unix - - n - 1 anvil
123 scache unix - - n - 1 scache
124 ${cfg.extraMasterConf}
125 '';
126
127 aliases =
128 optionalString (cfg.postmasterAlias != "") ''
129 postmaster: ${cfg.postmasterAlias}
130 ''
131 + optionalString (cfg.rootAlias != "") ''
132 root: ${cfg.rootAlias}
133 ''
134 + cfg.extraAliases
135 ;
136
137 aliasesFile = pkgs.writeText "postfix-aliases" aliases;
138 virtualFile = pkgs.writeText "postfix-virtual" cfg.virtual;
139 mainCfFile = pkgs.writeText "postfix-main.cf" mainCf;
140 masterCfFile = pkgs.writeText "postfix-master.cf" masterCf;
141
142in
143
144{
145
146 ###### interface
147
148 options = {
149
150 services.postfix = {
151
152 enable = mkOption {
153 default = false;
154 description = "Whether to run the Postfix mail server.";
155 };
156
157 setSendmail = mkOption {
158 default = true;
159 description = "Whether to set the system sendmail to postfix's.";
160 };
161
162 user = mkOption {
163 default = "postfix";
164 description = "What to call the Postfix user (must be used only for postfix).";
165 };
166
167 group = mkOption {
168 default = "postfix";
169 description = "What to call the Postfix group (must be used only for postfix).";
170 };
171
172 setgidGroup = mkOption {
173 default = "postdrop";
174 description = "
175 How to call postfix setgid group (for postdrop). Should
176 be uniquely used group.
177 ";
178 };
179
180 networks = mkOption {
181 default = null;
182 example = ["192.168.0.1/24"];
183 description = "
184 Net masks for trusted - allowed to relay mail to third parties -
185 hosts. Leave empty to use mynetworks_style configuration or use
186 default (localhost-only).
187 ";
188 };
189
190 networksStyle = mkOption {
191 default = "";
192 description = "
193 Name of standard way of trusted network specification to use,
194 leave blank if you specify it explicitly or if you want to use
195 default (localhost-only).
196 ";
197 };
198
199 hostname = mkOption {
200 default = "";
201 description ="
202 Hostname to use. Leave blank to use just the hostname of machine.
203 It should be FQDN.
204 ";
205 };
206
207 domain = mkOption {
208 default = "";
209 description ="
210 Domain to use. Leave blank to use hostname minus first component.
211 ";
212 };
213
214 origin = mkOption {
215 default = "";
216 description ="
217 Origin to use in outgoing e-mail. Leave blank to use hostname.
218 ";
219 };
220
221 destination = mkOption {
222 default = null;
223 example = ["localhost"];
224 description = "
225 Full (!) list of domains we deliver locally. Leave blank for
226 acceptable Postfix default.
227 ";
228 };
229
230 relayDomains = mkOption {
231 default = null;
232 example = ["localdomain"];
233 description = "
234 List of domains we agree to relay to. Default is the same as
235 destination.
236 ";
237 };
238
239 relayHost = mkOption {
240 default = "";
241 description = "
242 Mail relay for outbound mail.
243 ";
244 };
245
246 lookupMX = mkOption {
247 default = false;
248 description = "
249 Whether relay specified is just domain whose MX must be used.
250 ";
251 };
252
253 postmasterAlias = mkOption {
254 default = "root";
255 description = "Who should receive postmaster e-mail.";
256 };
257
258 rootAlias = mkOption {
259 default = "";
260 description = "
261 Who should receive root e-mail. Blank for no redirection.
262 ";
263 };
264
265 extraAliases = mkOption {
266 default = "";
267 description = "
268 Additional entries to put verbatim into aliases file, cf. man-page aliases(8).
269 ";
270 };
271
272 extraConfig = mkOption {
273 default = "";
274 description = "
275 Extra lines to be added verbatim to the main.cf configuration file.
276 ";
277 };
278
279 sslCert = mkOption {
280 default = "";
281 description = "SSL certificate to use.";
282 };
283
284 sslCACert = mkOption {
285 default = "";
286 description = "SSL certificate of CA.";
287 };
288
289 sslKey = mkOption {
290 default = "";
291 description = "SSL key to use.";
292 };
293
294 recipientDelimiter = mkOption {
295 default = "";
296 example = "+";
297 description = "
298 Delimiter for address extension: so mail to user+test can be handled by ~user/.forward+test
299 ";
300 };
301
302 virtual = mkOption {
303 default = "";
304 description = "
305 Entries for the virtual alias map, cf. man-page virtual(8).
306 ";
307 };
308
309 extraMasterConf = mkOption {
310 default = "";
311 example = "submission inet n - n - - smtpd";
312 description = "Extra lines to append to the generated master.cf file.";
313 };
314
315 };
316
317 };
318
319
320 ###### implementation
321
322 config = mkIf config.services.postfix.enable {
323
324 environment = {
325 etc = singleton
326 { source = "/var/postfix/conf";
327 target = "postfix";
328 };
329
330 # This makes comfortable for root to run 'postqueue' for example.
331 systemPackages = [ pkgs.postfix ];
332 };
333
334 services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
335 program = "sendmail";
336 source = "${pkgs.postfix}/bin/sendmail";
337 owner = "nobody";
338 group = "postdrop";
339 setuid = false;
340 setgid = true;
341 };
342
343 users.extraUsers = singleton
344 { name = user;
345 description = "Postfix mail server user";
346 uid = config.ids.uids.postfix;
347 group = group;
348 };
349
350 users.extraGroups =
351 [ { name = group;
352 gid = config.ids.gids.postfix;
353 }
354 { name = setgidGroup;
355 gid = config.ids.gids.postdrop;
356 }
357 ];
358
359 jobs.postfix =
360 # I copy _lots_ of shipped configuration filed
361 # that can be left as is. I am afraid the exact
362 # will list slightly change in next Postfix
363 # release, so listing them all one-by-one in an
364 # accurate way is unlikely to be better.
365 { description = "Postfix mail server";
366
367 wantedBy = [ "multi-user.target" ];
368 after = [ "network.target" ];
369
370 daemonType = "fork";
371
372 preStart = ''
373 if ! [ -d /var/spool/postfix ]; then
374 ${pkgs.coreutils}/bin/mkdir -p /var/spool/mail /var/postfix/conf /var/postfix/queue
375 fi
376
377 ${pkgs.coreutils}/bin/chown -R ${user}:${group} /var/postfix
378 ${pkgs.coreutils}/bin/chown -R ${user}:${setgidGroup} /var/postfix/queue
379 ${pkgs.coreutils}/bin/chmod -R ug+rwX /var/postfix/queue
380 ${pkgs.coreutils}/bin/chown root:root /var/spool/mail
381 ${pkgs.coreutils}/bin/chmod a+rwxt /var/spool/mail
382 ${pkgs.coreutils}/bin/ln -sf /var/spool/mail /var/mail
383
384 ln -sf "${pkgs.postfix}/etc/postfix/"* /var/postfix/conf
385
386 ln -sf ${aliasesFile} /var/postfix/conf/aliases
387 ln -sf ${virtualFile} /var/postfix/conf/virtual
388 ln -sf ${mainCfFile} /var/postfix/conf/main.cf
389 ln -sf ${masterCfFile} /var/postfix/conf/master.cf
390
391 ${pkgs.postfix}/sbin/postalias -c /var/postfix/conf /var/postfix/conf/aliases
392 ${pkgs.postfix}/sbin/postmap -c /var/postfix/conf /var/postfix/conf/virtual
393
394 ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf start
395 '';
396
397 preStop = ''
398 ${pkgs.postfix}/sbin/postfix -c /var/postfix/conf stop
399 '';
400
401 };
402
403 };
404
405}