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 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 # K-9 Mail 5.600 still requires this..
297 milter_macro_daemon_name = "ORIGINATING";
298 smtpd_helo_restrictions = [
299 "permit_sasl_authenticated"
300 ] ++ postfix.config.smtpd_helo_restrictions;
301 smtpd_relay_restrictions = [
302 # SASL authorizes to send to the world
303 "permit_sasl_authenticated"
306 smtpd_sasl_auth_enable = true;
307 smtpd_sasl_type = "dovecot";
308 smtpd_sasl_path = "private/auth";
309 smtpd_sasl_local_domain = "";
310 # Offer SASL authentication only after a TLS-encrypted session has been established
311 smtpd_tls_auth_only = true;
312 smtpd_sasl_tls_security_options = [ "noanonymous" ];
313 # Do not put SASL logins in mail headers
314 smtpd_sasl_authenticated_header = false;
315 # Who cares about (old) Outlook
316 broken_sasl_auth_clients = false;
317 smtpd_sender_restrictions = [
318 "reject_non_fqdn_sender"
319 # Check that the SASL user is using only its own
320 # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
321 "reject_sender_login_mismatch"
324 # No X.509 certificates for users, for now
325 smtpd_tls_ask_ccert = false;
326 smtpd_tls_ccert_verifydepth = 0;
327 smtpd_tls_loglevel = 1;
328 smtpd_tls_req_ccert = false;
329 cleanup_service_name = "submissions-header-cleanup";
332 submissions-header-cleanup = {
338 header_checks = "pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
339 # Removes sensitive headers from mails handed in via the submission or smtps port.
340 # See https://thomas-leister.de/mailserver-debian-stretch/
341 # Uses "pcre" style regex.
344 /^User-Agent:/ IGNORE
345 /^X-Enigmail:/ IGNORE
347 /^X-Originating-IP:/ IGNORE
353 #spfcheck unix - n n - 0 spawn
354 # user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
355 # -o smtpd_sender_restrictions=reject_sender_login_mismatch
356 # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
357 # -o cleanup_service_name=submissions-header-cleanup
358 #spfcheck unix - n n - 0 spawn
359 # user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
360 #uucp unix - n n - - pipe
361 # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
362 #smtp inet n - - - - smtpd
363 # -o cleanup_service_name=pre-cleanup
364 # -o content_filter=amavis:[127.0.0.1]:10024
365 # -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
366 # -o receive_override_options=no_address_mappings
367 #amavis unix - - n - 2 lmtp
368 # -o lmtp_data_done_timeout=1200
369 # -o lmtp_send_xforward_command=yes
370 # -o lmtp_tls_note_starttls_offer=no
371 #127.0.0.1:10025 inet n - n - - smtpd
373 # -o local_header_rewrite_clients=
374 # -o local_recipient_maps=
375 # -o mynetworks=127.0.0.0/8
376 # -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
377 # -o relay_recipient_maps=
378 # -o smtpd_client_connection_count_limit=0
379 # -o smtpd_client_connection_rate_limit=0
380 # -o smtpd_client_restrictions=permit_mynetworks,reject
381 # -o smtpd_data_restrictions=reject_unauth_pipelining
382 # -o smtpd_delay_reject=no
383 # -o smtpd_end_of_data_restrictions=
384 # -o smtpd_error_sleep_time=0
385 # -o smtpd_hard_error_limit=1000
386 # -o smtpd_helo_restrictions=
388 # -o smtpd_recipient_restrictions=permit_mynetworks,reject
389 # -o smtpd_restriction_classes=
390 # -o smtpd_sender_restrictions=
391 # -o smtpd_soft_error_limit=1001
392 # -o strict_rfc821_envelopes=yes
393 #submission inet n - - - - smtpd
394 # -o cleanup_service_name=pre-cleanup
395 # -o content_filter=amavis:[127.0.0.1]:10024
396 # -o milter_macro_daemon_name=ORIGINATING
397 # -o receive_override_options=no_address_mappings
398 # -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
399 # -o smtpd_tls_ask_ccert=yes
400 # -o smtpd_tls_auth_only=yes
401 # -o smtpd_tls_ccert_verifydepth=2
402 # -o smtpd_tls_loglevel=1
403 # -o smtpd_tls_req_ccert=yes
404 # -o smtpd_tls_security_level=encrypt
405 #smtps inet n - - - - smtpd
406 # -o milter_macro_daemon_name=ORIGINATING
407 # -o smtpd_client_restrictions=permit_sasl_authenticated,reject
408 # -o smtpd_sasl_auth_enable=yes
409 # -o smtpd_tls_ask_ccert=yes
410 # -o smtpd_tls_auth_only=yes
411 # -o smtpd_tls_ccert_verifydepth=0
412 # -o smtpd_tls_loglevel=1
413 # -o smtpd_tls_req_ccert=no
414 # -o smtpd_tls_security_level=encrypt
415 # -o smtpd_tls_wrappermode=yes
416 #pickup fifo n - - 60 1 pickup
417 # -o cleanup_service_name=pre-cleanup
418 # -o content_filter=amavis:[127.0.0.1]:10024
419 #pre-cleanup unix n - - - 0 cleanup
420 # -o virtual_alias_maps=
421 #cleanup unix n - - - 0 cleanup
422 # -o mime_header_checks=
423 # -o nested_header_checks=
427 #sympa unix - n n - - pipe
428 # flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
429 #sympabounce unix - n n - - pipe
430 # flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
433 #noclue unix - n n - - pipe
434 # flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}