1 { pkgs, lib, config, ... }:
3 inherit (builtins) attrNames concatStringsSep readFile toPath;
5 inherit (pkgs.lib) loadFile unlines unwords unlinesAttrs;
6 inherit (config) networking users;
7 inherit (config.services) postfix dovecot2 openldap;
14 imports = map (domain: (./postfix + "/${domain}.nix")) domains;
17 tls_server_sni_maps = lib.mkOption {
18 type = types.attrsOf (types.listOf types.path);
20 apply = m: pkgs.writeText "sni" (lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: x509: ''
21 ${domain} ${unwords x509}
27 users.groups.acme.members = [ postfix.user ];
28 systemd.services.postfix = {
29 wants = ["openldap.service"];
30 after = ["openldap.service"];
32 install -m 400 -o root -g root ${postfix.tls_server_sni_maps} /run/keys/postfix-sni
33 ${pkgs.postfix}/bin/postmap -F hash:/run/keys/postfix-sni
36 networking.nftables.ruleset = ''
37 add rule inet filter net2fw tcp dport 25 counter accept comment "SMTP"
38 add rule inet filter net2fw tcp dport 465 counter accept comment "submissions"
39 add rule inet filter fw2net meta skuid ${postfix.user} tcp dport 25 counter accept comment "SMTP"
43 networksStyle = "host";
44 hostname ="${networking.hostName}.${networking.domain}";
45 domain = networking.domain;
46 origin = "$myhostname";
49 "localhost.localdomain"
52 postmasterAlias = "root";
53 rootAlias = "root@${networking.domain}";
54 sslKey = "/var/lib/acme/${networking.domain}/key.pem";
55 sslCert = "/var/lib/acme/${networking.domain}/fullchain.pem";
61 # Parse the extension in email address, eg. contact+extension@
62 recipientDelimiter = "+";
64 debug_peer_level = "4";
66 #"chomsky.autogeree.net"
68 #"mail.sourcephile.fr"
72 # Sending to the world
74 # Appending .domain is the MUA's job
75 append_dot_mydomain = false;
76 smtp_body_checks = "";
77 #smtp_cname_overrides_servername = false;
78 smtp_connect_timeout = "60s";
79 #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
80 smtp_mime_header_checks = "";
81 smtp_nested_header_checks = "";
82 smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
83 #smtp_tls_fingerprint_digest = "sha1";
84 smtp_tls_loglevel = "1";
85 #smtp_tls_note_starttls_offer = true;
86 #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
87 # Only allow TLSv* protocols
88 smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
89 #smtp_tls_scert_verifydepth = "5";
90 #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
91 smtp_tls_security_level = "may";
92 smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
93 #smtp_tls_session_cache_timeout = "3600s";
94 #smtp_tls_verify_cert_match = "hostname";
97 # Receiving from the world
99 message_size_limit = "20480000";
100 maximal_queue_lifetime = "5d";
101 default_extra_recipient_limit = "5000";
102 line_length_limit = "2048";
103 duplicate_filter_limit = "5000";
104 # Stops mail from poorly written software
105 strict_rfc821_envelopes = true;
106 mime_header_checks = [];
107 milter_header_checks = [];
108 nested_header_checks = [];
111 permit_mx_backup_networks = [];
112 propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
113 #masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
114 #masquerade_domains = "";
115 #masquerade_exceptions = "root";
117 # Stops some techniques used to harvest email addresses
118 disable_vrfy_command = true;
119 enable_long_queue_ids = false;
120 # Useful to test restrictions
121 smtpd_authorized_xclient_hosts = "127.0.0.1";
122 smtpd_banner = "$myhostname ESMTP $mail_name (NixOS)";
123 smtpd_client_connection_count_limit = "50";
124 smtpd_client_connection_rate_limit = "0";
125 smtpd_client_event_limit_exceptions = "$mynetworks";
126 smtpd_client_message_rate_limit = "0";
127 smtpd_client_new_tls_session_rate_limit = "0";
128 smtpd_client_port_logging = false;
129 smtpd_client_recipient_rate_limit = "0";
131 smtpd_error_sleep_time = "5";
132 # Needed to enforce reject_unknown_helo_hostname
133 smtpd_helo_required = true;
134 smtpd_helo_restrictions = [
135 "reject_invalid_helo_hostname"
136 "reject_non_fqdn_helo_hostname"
137 # Don't talk to mail systems that don't know their own hostname.
138 "reject_unknown_helo_hostname"
141 smtpd_client_restrictions = [
143 # Set in postfix/*.nix and used in submissions/smptd
144 # with reject_sender_login_mismatch
145 smtpd_sender_login_maps = [];
146 smtpd_sender_restrictions = [
147 "reject_non_fqdn_sender"
150 smtpd_reject_unlisted_recipient = true;
151 # Check the RCPT TO, before smtpd_recipient_restrictions
152 # Restrictions based on what is allowed or not,
153 # these are applied before smtpd_recipient_restrictions
154 smtpd_relay_restrictions = [
156 # Check the recipient's address in virtual_mailbox_domains and virtual_mailbox_maps
157 "permit_auth_destination"
158 # The world is only authorized to use our relay for the above destinations.
161 # Restrictions based on what is working or not
162 smtpd_recipient_restrictions = [
163 # Reject if the domain is not fully qualified
164 "reject_non_fqdn_recipient"
165 # Reject if the domain is not working, even before bothering to check the address
166 "reject_unknown_recipient_domain"
167 # Reject if the address is not working
168 # WARNING: this does not work if the recipient is greylisting.
169 # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
170 # postmap -s btree:/var/lib/postfix/data/verify_cache
171 #"reject_unverified_recipient"
174 # Trust the verify database
175 #unverified_recipient_reject_code = "550";
176 smtpd_data_restrictions = [
177 # Force the smtpd's client to wait OK before sending
178 "reject_unauth_pipelining"
181 smtpd_end_of_data_restrictions = [
182 # Enforce mail volume quota via policy service callouts.
183 #check_policy_service unix:private/policy
186 smtpd_peername_lookup = true;
187 smtpd_recipient_limit = "5000";
188 smtpd_recipient_overshoot_limit = "5000";
189 #smtpd_restriction_classes = "";
190 #smtpd_sasl_auth_enable = true;
191 #smtpd_sasl_path = "private/auth";
192 #smtpd_sasl_security_options = "noanonymous";
193 #smtpd_sasl_type = "dovecot";
194 smtpd_starttls_timeout = "300s";
195 #smtpd_tls_always_issue_session_ids = true;
196 #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
197 smtpd_tls_ask_ccert = false;
198 #smtpd_tls_ccert_verifydepth = "5";
199 smtpd_tls_ciphers = "high";
200 smtpd_tls_eecdh_grade = "auto";
201 # Disable weak ciphers as reported by https://ssl-tools.net
202 # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
203 smtpd_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
204 smtpd_tls_fingerprint_digest = "sha512";
205 # Log only a summary message on TLS handshake completion
206 smtpd_tls_loglevel = "1";
207 smtpd_tls_mandatory_ciphers = "high";
208 smtpd_tls_mandatory_protocols = [ "!SSLv2" "!SSLv3" ];
210 smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
211 #smtpd_tls_received_header = false;
212 smtpd_tls_req_ccert = false;
213 # Postfix 2.3 and later
215 # Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
216 # encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
217 # SMTP server. Instead, this option should be used only on dedicated servers.
218 smtpd_tls_security_level = "may";
219 smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
220 #smtpd_tls_session_cache_timeout = "3600s";
221 #smtpd_tls_chain_files =
224 #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
225 # This is where to put backup MX domains
227 relay_recipient_maps = [];
229 # Use a non blocking source of randomness
230 tls_random_source = "dev:/dev/urandom";
231 # Map each domain to a specific X.509 certificate
232 tls_server_sni_maps = "hash:/run/keys/postfix-sni";
234 # Only explicitely aliased accounts have a mail, not all the passwd
235 local_recipient_maps = "$alias_maps";
236 # Note that the local transport rewrites the envelope recipient
237 # according to the alias_maps, and thus the aliasing is transparent
238 # to the nexthop (eg. dovecot)
239 #local_transport = local:$myhostname
240 # No console bell on new mail
244 "$home/.forward''${recipient_delimiter}''${extension}"
249 # Filled by the postfix/*.nix
250 virtual_mailbox_domains = [];
251 # Completed by the postfix/*.nix
252 virtual_mailbox_maps = [
253 "hash:/etc/postfix/virtual"
255 virtual_transport = "lmtp:unix:private/dovecot-lmtp";
257 dovecot_destination_recipient_limit = "1";
258 virtual_transport = "dovecot";
261 # There is no fallback
262 fallback_transport = "";
264 virtualMapType = "hash";
269 then concatStringsSep "," value
271 if value == true then "yes"
272 else if value == false then "no"
274 mkKeyVal = opt: val: [ "-o" (opt + "=" + mkVal val) ];
275 mkArgs = args: lib.concatLists (lib.mapAttrsToList mkKeyVal args);
279 cleanup_service_name = "submissions-header-cleanup";
282 # Implicit TLS on port 465
283 # https://tools.ietf.org/html/rfc8314#section-3.3
289 syslog_name = "postfix/submissions";
290 # Implicit TLS, not STARTTLS
291 smtpd_tls_wrappermode = true;
292 smtpd_tls_mandatory_protocols = [
294 # FIXME: to be removed when K-9 Mail will support TLSv1.3,
295 # K-9 Mail 5.600 does not.
298 milter_macro_daemon_name = "ORIGINATING";
299 smtpd_helo_restrictions = [
300 "permit_sasl_authenticated"
301 ] ++ postfix.config.smtpd_helo_restrictions;
302 smtpd_relay_restrictions = [
303 # SASL authorizes to send to the world
304 "permit_sasl_authenticated"
307 smtpd_sasl_auth_enable = true;
308 smtpd_sasl_type = "dovecot";
309 smtpd_sasl_path = "private/auth";
310 smtpd_sasl_local_domain = "";
311 # Offer SASL authentication only after a TLS-encrypted session has been established
312 smtpd_tls_auth_only = true;
313 smtpd_sasl_tls_security_options = [ "noanonymous" ];
314 # Do not put SASL logins in mail headers
315 smtpd_sasl_authenticated_header = false;
316 # Who cares about (old) Outlook
317 broken_sasl_auth_clients = false;
318 smtpd_sender_restrictions = [
319 "reject_non_fqdn_sender"
320 # Check that the SASL user is using only its own
321 # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
322 "reject_sender_login_mismatch"
325 # No X.509 certificates for users, for now
326 smtpd_tls_ask_ccert = false;
327 smtpd_tls_ccert_verifydepth = 0;
328 smtpd_tls_loglevel = 1;
329 smtpd_tls_req_ccert = false;
330 cleanup_service_name = "submissions-header-cleanup";
333 submissions-header-cleanup = {
339 header_checks = "pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
340 # Removes sensitive headers from mails handed in via the submission or smtps port.
341 # See https://thomas-leister.de/mailserver-debian-stretch/
342 # Uses "pcre" style regex.
345 /^User-Agent:/ IGNORE
346 /^X-Enigmail:/ IGNORE
348 /^X-Originating-IP:/ IGNORE
354 #spfcheck unix - n n - 0 spawn
355 # user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
356 # -o smtpd_sender_restrictions=reject_sender_login_mismatch
357 # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
358 # -o cleanup_service_name=submissions-header-cleanup
359 #spfcheck unix - n n - 0 spawn
360 # user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
361 #uucp unix - n n - - pipe
362 # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
363 #smtp inet n - - - - smtpd
364 # -o cleanup_service_name=pre-cleanup
365 # -o content_filter=amavis:[127.0.0.1]:10024
366 # -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
367 # -o receive_override_options=no_address_mappings
368 #amavis unix - - n - 2 lmtp
369 # -o lmtp_data_done_timeout=1200
370 # -o lmtp_send_xforward_command=yes
371 # -o lmtp_tls_note_starttls_offer=no
372 #127.0.0.1:10025 inet n - n - - smtpd
374 # -o local_header_rewrite_clients=
375 # -o local_recipient_maps=
376 # -o mynetworks=127.0.0.0/8
377 # -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
378 # -o relay_recipient_maps=
379 # -o smtpd_client_connection_count_limit=0
380 # -o smtpd_client_connection_rate_limit=0
381 # -o smtpd_client_restrictions=permit_mynetworks,reject
382 # -o smtpd_data_restrictions=reject_unauth_pipelining
383 # -o smtpd_delay_reject=no
384 # -o smtpd_end_of_data_restrictions=
385 # -o smtpd_error_sleep_time=0
386 # -o smtpd_hard_error_limit=1000
387 # -o smtpd_helo_restrictions=
389 # -o smtpd_recipient_restrictions=permit_mynetworks,reject
390 # -o smtpd_restriction_classes=
391 # -o smtpd_sender_restrictions=
392 # -o smtpd_soft_error_limit=1001
393 # -o strict_rfc821_envelopes=yes
394 #submission inet n - - - - smtpd
395 # -o cleanup_service_name=pre-cleanup
396 # -o content_filter=amavis:[127.0.0.1]:10024
397 # -o milter_macro_daemon_name=ORIGINATING
398 # -o receive_override_options=no_address_mappings
399 # -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
400 # -o smtpd_tls_ask_ccert=yes
401 # -o smtpd_tls_auth_only=yes
402 # -o smtpd_tls_ccert_verifydepth=2
403 # -o smtpd_tls_loglevel=1
404 # -o smtpd_tls_req_ccert=yes
405 # -o smtpd_tls_security_level=encrypt
406 #smtps inet n - - - - smtpd
407 # -o milter_macro_daemon_name=ORIGINATING
408 # -o smtpd_client_restrictions=permit_sasl_authenticated,reject
409 # -o smtpd_sasl_auth_enable=yes
410 # -o smtpd_tls_ask_ccert=yes
411 # -o smtpd_tls_auth_only=yes
412 # -o smtpd_tls_ccert_verifydepth=0
413 # -o smtpd_tls_loglevel=1
414 # -o smtpd_tls_req_ccert=no
415 # -o smtpd_tls_security_level=encrypt
416 # -o smtpd_tls_wrappermode=yes
417 #pickup fifo n - - 60 1 pickup
418 # -o cleanup_service_name=pre-cleanup
419 # -o content_filter=amavis:[127.0.0.1]:10024
420 #pre-cleanup unix n - - - 0 cleanup
421 # -o virtual_alias_maps=
422 #cleanup unix n - - - 0 cleanup
423 # -o mime_header_checks=
424 # -o nested_header_checks=
428 #sympa unix - n n - - pipe
429 # flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
430 #sympabounce unix - n n - - pipe
431 # flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
434 #noclue unix - n n - - pipe
435 # flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}