1 {pkgs, lib, config, nodes, ...}:
2 let inherit (builtins) attrNames toFile;
4 inherit (pkgs.lib) unlines unlinesAttrs;
5 inherit (config.services) x509 postfix dovecot2 postgrey openldap;
6 unwords = lib.concatStringsSep " ";
7 when = x: y: if x == null then "" else y;
9 submissionHeaderCleanupRules = pkgs.writeText "submission_header_cleanup_rules" ''
10 # Removes sensitive headers from mails handed in via the submission or smtps port.
11 # See https://thomas-leister.de/mailserver-debian-stretch/
12 # Uses "pcre" style regex.
18 /^X-Originating-IP:/ IGNORE
22 options.services.postfix.aliases = lib.mkOption {
23 type = with types; attrsOf (listOf str);
25 example = { "root@${config.networking.domain}" = [
26 "user1@${config.networking.domain}"
27 "user2@${config.networking.domain}"
29 "@example.coop" = ["user1@${config.networking.domain}"];
33 systemd.services.postfix.after =
34 if x509.scheme == "letsencrypt"
35 then [ "nginx.service" ] # XXX: not sure if this is enough
39 #hostname = config.networking.domain;
40 #domain = "localdomain";
41 networksStyle = "host";
42 #mapFiles."valias" = toFile "valias" (unlines (all_valiases_postfix ++ catchAllPostfix));
43 # See https://blog.grimneko.de/2011/12/24/a-bunch-of-tips-for-improving-your-postfix-setup/
44 # for details on how this file looks. By using the same file as valias,
45 # every alias is uniquely owned by its user.
46 # The user's own address is already in all_valiases_postfix.
47 #mapFiles."vaccounts" = toFile "vaccounts" (unlines all_valiases_postfix);
48 mapFiles."virtual_alias_maps" =
49 toFile "virtual_alias_maps"
50 (unlinesAttrs (dom: {accounts, ...}:
51 unlinesAttrs (user: acct:
52 unlines (map (from: "${from} ${user}@${dom}") acct.aliases)
57 (from: to: "${from} ${unwords to}")
59 mapFiles."ldap-virtual_alias_maps.cf" =
60 toFile "ldap-virtual_alias_maps.cf" ''
63 server_host = ldapi://
66 search_base = ou=posix,${openldap.domainSuffix}
69 query_filter = (&(mailAlias=%s)(mailEnabled=TRUE))
71 result_attribute = mail
75 #enableSubmission = true;
79 "localhost.localdomain"
80 config.networking.hostName
81 "${config.networking.hostName}.localdomain"
87 recipientDelimiter = "+";
89 # Appending .domain is the MUA's job
90 append_dot_mydomain = false;
91 # No console bell on new mail
94 #content_filter = "amavisfeed:[127.0.0.1]:10024";
95 #debug_peer_level = 4;
96 #debug_peer_list = ".$myhostname";
97 default_extra_recipient_limit = "5000";
98 # Uncomment the next line to generate "delayed mail" warnings
99 #delay_warning_time = "4h";
100 # Stops some techniques used to harvest email addresses
101 disable_vrfy_command = true;
102 duplicate_filter_limit = "5000";
103 enable_long_queue_ids = false;
104 # Pass unexisting $mydestination recipients to dovecot
105 fallback_transport = "lmtp:unix:private/dovecot-lmtp";
107 ''$home/.forward''${recipient_delimiter}''${extension}''
110 #header_checks = "regexp:/var/lib/postfix/conf/header_checks";
111 #inet_interfaces = "all";
112 line_length_limit = "2048";
114 # Let $fallback_transport check existence of recipients
115 local_recipient_maps = "";
116 #mail_spool_directory = "/var/spool/mail";
117 # NOTE: nixpkgs's default
118 #local_header_rewrite_clients = "";
119 #home_mailbox = "Maildir/";
120 #mailbox_command = ''
121 # ${pkgs.procmail}/bin/procmail -t -a "$SENDER" -a "$RECIPIENT" -a "$USER" -a "$EXTENSION" -a "$DOMAIN" -a "$ORIGINAL_RECIPIENT" "$HOME/.procmailrc"
123 mailbox_size_limit = "204800000";
125 masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
126 masquerade_domains = "";
127 masquerade_exceptions = "root";
128 maximal_queue_lifetime = "5d";
129 message_size_limit = "20480000";
130 mime_header_checks = "";
131 milter_header_checks = "";
132 nested_header_checks = "";
133 non_smtpd_milters = "";
134 parent_domain_matches_subdomains = [
136 #"fast_flush_domains"
138 #"permit_mx_backup_networks"
139 #"qmqpd_authorized_clients"
142 permit_mx_backup_networks = "";
143 #policy-spf_time_limit = "3600s";
144 propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
146 #receive_override_options = "no_address_mappings";
147 # no_unknown_recipient_checks
148 # Do not try to reject unknown recipients (SMTP server only).
149 # This is typically specified AFTER an external content filter.
150 # no_address_mappings
151 # Disable canonical address mapping, virtual alias map expansion,
152 # address masquerading, and automatic BCC (blind carbon-copy) recipients.
153 # This is typically specified BEFORE an external content filter (eg. amavis).
154 # no_header_body_checks
155 # Disable header/body_checks. This is typically specified AFTER
156 # an external content filter.
158 # Disable Milter (mail filter) applications.
159 # This is typically specified AFTER an external content filter.
160 # Parse the extension in email address, eg. contact+extension@
162 #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
163 # This is where to put backup MX domains
164 relay_domains = "$mydestination";
165 relay_recipient_maps = "";
166 smtp_body_checks = "";
167 #smtp_cname_overrides_servername = false;
168 smtp_connect_timeout = "60s";
169 #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
170 smtp_mime_header_checks = "";
171 smtp_nested_header_checks = "";
172 smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
173 #smtp_tls_fingerprint_digest = "sha1";
174 smtp_tls_loglevel = "1";
175 #smtp_tls_note_starttls_offer = true;
176 #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
177 # Only allow TLSv* protocols
178 smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
179 smtp_tls_scert_verifydepth = "5";
180 #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
181 smtp_tls_security_level = "may";
182 smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
183 #smtp_tls_session_cache_timeout = "3600s";
184 #smtp_tls_verify_cert_match = "hostname";
185 # Useful to test restrictions
186 smtpd_authorized_xclient_hosts = "127.0.0.1";
187 smtpd_banner = "${config.networking.fqdn} ESMTP $mail_name (NixOS)";
188 smtpd_client_connection_count_limit = "50";
189 smtpd_client_connection_rate_limit = "0";
190 smtpd_client_event_limit_exceptions = "$mynetworks";
191 smtpd_client_message_rate_limit = "0";
192 smtpd_client_new_tls_session_rate_limit = "0";
193 smtpd_client_port_logging = false;
194 smtpd_client_recipient_rate_limit = "0";
195 smtpd_client_restrictions = [
196 #"check_client_access hash:/var/lib/postfix/conf/client_blacklist"
198 smtpd_data_restrictions = [
199 "reject_unauth_pipelining"
200 # Force the smtp client to wait OK before sending
203 # Disable opportunistic encryption
204 smtpd_discard_ehlo_keywords = "starttls";
205 #smtpd_end_of_data_restrictions = "";
207 smtpd_error_sleep_time = "5";
208 smtpd_helo_required = true;
209 smtpd_helo_restrictions = [
210 "reject_invalid_helo_hostname"
211 "reject_non_fqdn_helo_hostname"
212 #"reject_unknown_helo_hostname"
213 # May be useful to fight spam
218 smtpd_peername_lookup = true;
219 smtpd_recipient_limit = "5000";
220 smtpd_recipient_overshoot_limit = "5000";
221 smtpd_recipient_restrictions = [
222 "reject_non_fqdn_recipient"
223 #"reject_invalid_hostname"
224 "reject_unknown_recipient_domain"
225 #"reject_non_fqdn_sender"
226 "reject_unauth_pipelining"
227 #"check_policy_service inet:localhost:12340"
230 "permit_tls_clientcerts"
231 "permit_sasl_authenticated"
232 "reject_unverified_recipient"
233 # $fallback_transport is responsible of checking the existence of the recipient
234 # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
235 # postmap -s btree:/var/lib/postfix/data/verify_cache
236 # Bypass SPF check and postgrey if the recipient is not for us or someone in backup_mx
237 "reject_unauth_destination"
239 #"check_policy_service unix:private/spfcheck"
240 # Greylisting using postgrey
241 "check_policy_service unix:${postgrey.socket.path}"
242 # Once postgrey passed, permit what is for us
243 "permit_auth_destination"
245 #"reject_unknown_sender_domain"
246 # Maybe better in smtpd_sender_restrictions
247 #"reject_rbl_client bl.spamcop.net"
248 #"reject_rbl_client list.dsbl.org"
249 #"reject_rbl_client zen.spamhaus.org"
250 #"reject_rbl_client dnsbl.sorbs.net"
252 smtpd_relay_restrictions = [
254 "permit_sasl_authenticated"
255 # NOTE: permit auth through dovecot's SASL
256 "reject_unauth_destination"
258 #smtpd_restriction_classes = "";
259 broken_sasl_auth_clients = false;
260 #smtpd_sasl_auth_enable = true;
261 #smtpd_sasl_path = "private/auth";
262 #smtpd_sasl_security_options = "noanonymous";
263 #smtpd_sasl_type = "dovecot";
264 smtpd_sender_restrictions = [
266 "permit_tls_clientcerts"
267 "permit_sasl_authenticated"
268 # NOTE: permit auth through dovecot's SASL
269 #"check_sender_access hash:/var/lib/postfix/conf/sender_access"
270 "reject_unauth_pipelining"
271 "reject_non_fqdn_sender"
272 #"reject_sender_login_mismatch"
273 #"reject_unknown_sender_domain"
276 smtpd_starttls_timeout = "300s";
277 #smtpd_tls_always_issue_session_ids = true;
278 # No SASL AUTH without TLS
279 smtpd_tls_auth_only = true;
280 #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
281 smtpd_tls_ask_ccert = false;
282 #smtpd_tls_ccert_verifydepth = "5";
283 smtpd_tls_ciphers = "high";
284 smtpd_tls_eecdh_grade = "ultra";
285 # Disable weak ciphers as reported by https://ssl-tools.net
286 # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
287 smtpd_tls_exclude_ciphers = [ "RC4" "aNULL" ];
288 smtpd_tls_fingerprint_digest = "sha512";
289 # Log only a summary message on TLS handshake completion
290 smtpd_tls_loglevel = "1";
291 smtpd_tls_mandatory_ciphers = "high";
292 smtpd_tls_mandatory_protocols = "TLSv1";
294 smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
295 #smtpd_tls_received_header = false;
296 smtpd_tls_req_ccert = false;
297 # Postfix 2.3 and later
299 # Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
300 # encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
301 # SMTP server. Instead, this option should be used only on dedicated servers.
302 smtpd_tls_security_level = "may";
303 smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
304 #smtpd_tls_session_cache_timeout = "3600s";
305 # Stops mail from poorly written software
306 strict_rfc821_envelopes = true;
307 #sympa_destination_recipient_limit = "1";
308 #sympabounce_destination_recipient_limit = "1";
309 # postconf(5) discourages to change this
310 #tls_high_cipherlist = "AES256-SHA";
311 #tls_random_bytes = "32";
312 # Must not be in a chroot
313 #tls_random_exchange_name = "$data_directory/prng_exch";
314 #tls_random_prng_update_period = "3600s";
315 #tls_random_reseed_period = "3600s";
316 # Use a non blocking source of randomness
317 tls_random_source = "dev:/dev/urandom";
320 #"hash:/etc/postfix/transport-dovecot"
321 #"hash:/etc/postfix/$mydomain/transport"
322 #"hash:/etc/dovecot/transport"
323 #"regexp:/etc/sympa/transport"
325 # Rejects immediately what $fallback_transport rejects
326 unverified_recipient_reject_code = "550";
327 # Do not specify virtual alias domain names in mydestination
328 # or relay_domains configuration parameters
330 # With a virtual alias domain, the Postfix SMTP server
331 # accepts mail for known-user@virtual-alias.domain, and
332 # rejects mail for unknown-user@virtual-alias.domain as
334 virtual_alias_domains = [];
335 virtual_alias_maps = [
336 "hash:/etc/postfix/virtual_alias_maps"
337 "ldap:/etc/postfix/ldap-virtual_alias_maps.cf"
338 #"hash:/etc/postfix/virtual_alias-dovecot"
339 #"hash:/var/lib/postfix/conf/valias"
340 #"regexp:/etc/sympa/virtual_alias"
342 #virtual_uid_maps = "static:5000";
343 #virtual_gid_maps = "static:5000";
344 #virtual_mailbox_base = dovecot2.mailDir;
345 virtual_mailbox_domains = toFile "virtual_mailbox_domains" (unlines (attrNames dovecot2.domains));
346 #virtual_mailbox_maps = "hash:/etc/postfix/virtual_mailbox_maps";
347 virtual_transport = "lmtp:unix:private/dovecot-lmtp";
349 #submissionOptions = {
350 # smtpd_tls_security_level = "encrypt";
351 # smtpd_sasl_auth_enable = "yes";
352 # smtpd_sasl_type = "dovecot";
353 # smtpd_sasl_path = "private/auth";
354 # smtpd_sasl_security_options = "noanonymous";
355 # smtpd_sasl_local_domain = "$myhostname";
356 # smtpd_client_restrictions = "permit_sasl_authenticated,reject";
357 # smtpd_sender_login_maps = "hash:/etc/postfix/vaccounts";
358 # smtpd_sender_restrictions = "reject_sender_login_mismatch";
359 # smtpd_recipient_restrictions = "reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject";
360 # cleanup_service_name = "submission-header-cleanup";
363 #spfcheck unix - n n - 0 spawn
364 # user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
365 465 inet n - - - - smtpd
366 -o milter_macro_daemon_name=ORIGINATING
367 -o smtpd_client_restrictions=permit_sasl_authenticated,reject
368 -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
369 -o smtpd_sasl_auth_enable=yes
370 -o smtpd_sasl_local_domain=$myhostname
371 -o smtpd_sasl_path=private/auth
372 -o smtpd_sasl_security_options=noanonymous
373 -o smtpd_sasl_type=dovecot
374 -o smtpd_tls_ask_ccert=no
375 -o smtpd_tls_auth_only=yes
376 -o smtpd_tls_ccert_verifydepth=0
377 -o smtpd_tls_loglevel=1
378 -o smtpd_tls_req_ccert=no
379 -o smtpd_tls_security_level=encrypt
380 -o smtpd_tls_wrappermode=yes
381 # -o smtpd_sender_restrictions=reject_sender_login_mismatch
382 # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
383 # -o cleanup_service_name=submission-header-cleanup
384 submission-header-cleanup unix n - n - 0 cleanup
385 -o header_checks=pcre:${submissionHeaderCleanupRules}
386 #spfcheck unix - n n - 0 spawn
387 # user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
388 #uucp unix - n n - - pipe
389 # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
390 #smtp inet n - - - - smtpd
391 # -o cleanup_service_name=pre-cleanup
392 # -o content_filter=amavis:[127.0.0.1]:10024
393 # -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
394 # -o receive_override_options=no_address_mappings
395 #amavis unix - - n - 2 lmtp
396 # -o lmtp_data_done_timeout=1200
397 # -o lmtp_send_xforward_command=yes
398 # -o lmtp_tls_note_starttls_offer=no
399 #127.0.0.1:10025 inet n - n - - smtpd
401 # -o local_header_rewrite_clients=
402 # -o local_recipient_maps=
403 # -o mynetworks=127.0.0.0/8
404 # -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
405 # -o relay_recipient_maps=
406 # -o smtpd_client_connection_count_limit=0
407 # -o smtpd_client_connection_rate_limit=0
408 # -o smtpd_client_restrictions=permit_mynetworks,reject
409 # -o smtpd_data_restrictions=reject_unauth_pipelining
410 # -o smtpd_delay_reject=no
411 # -o smtpd_end_of_data_restrictions=
412 # -o smtpd_error_sleep_time=0
413 # -o smtpd_hard_error_limit=1000
414 # -o smtpd_helo_restrictions=
416 # -o smtpd_recipient_restrictions=permit_mynetworks,reject
417 # -o smtpd_restriction_classes=
418 # -o smtpd_sender_restrictions=
419 # -o smtpd_soft_error_limit=1001
420 # -o strict_rfc821_envelopes=yes
421 #submission inet n - - - - smtpd
422 # -o cleanup_service_name=pre-cleanup
423 # -o content_filter=amavis:[127.0.0.1]:10024
424 # -o milter_macro_daemon_name=ORIGINATING
425 # -o receive_override_options=no_address_mappings
426 # -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
427 # -o smtpd_tls_ask_ccert=yes
428 # -o smtpd_tls_auth_only=yes
429 # -o smtpd_tls_ccert_verifydepth=2
430 # -o smtpd_tls_loglevel=1
431 # -o smtpd_tls_req_ccert=yes
432 # -o smtpd_tls_security_level=encrypt
433 #smtps inet n - - - - smtpd
434 # -o milter_macro_daemon_name=ORIGINATING
435 # -o smtpd_client_restrictions=permit_sasl_authenticated,reject
436 # -o smtpd_sasl_auth_enable=yes
437 # -o smtpd_tls_ask_ccert=yes
438 # -o smtpd_tls_auth_only=yes
439 # -o smtpd_tls_ccert_verifydepth=0
440 # -o smtpd_tls_loglevel=1
441 # -o smtpd_tls_req_ccert=no
442 # -o smtpd_tls_security_level=encrypt
443 # -o smtpd_tls_wrappermode=yes
444 #pickup fifo n - - 60 1 pickup
445 # -o cleanup_service_name=pre-cleanup
446 # -o content_filter=amavis:[127.0.0.1]:10024
447 #pre-cleanup unix n - - - 0 cleanup
448 # -o virtual_alias_maps=
449 #cleanup unix n - - - 0 cleanup
450 # -o mime_header_checks=
451 # -o nested_header_checks=
455 #sympa unix - n n - - pipe
456 # flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
457 #sympabounce unix - n n - - pipe
458 # flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
461 #noclue unix - n n - - pipe
462 # flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}