1 { pkgs, lib, config, ... }:
3 inherit (builtins) attrNames concatStringsSep readFile toPath;
4 inherit (builtins.extraBuiltins) pass;
6 inherit (pkgs.lib) loadFile unlines unwords unlinesAttrs;
7 inherit (config) networking users;
8 inherit (config.services) postfix dovecot2 openldap;
15 imports = map (domain: (./postfix + "/${domain}.nix")) domains;
18 tls_server_sni_maps = lib.mkOption {
19 type = types.attrsOf (types.listOf types.path);
21 apply = m: pkgs.writeText "sni" (lib.concatStringsSep "\n" (lib.mapAttrsToList (domain: x509: ''
22 ${domain} ${unwords x509}
28 users.groups.acme.members = [ postfix.user ];
29 systemd.services.postfix = {
30 wants = ["openldap.service"];
31 after = ["openldap.service"];
33 install -m 400 -o root -g root ${postfix.tls_server_sni_maps} /run/keys/postfix-sni
34 ${pkgs.postfix}/bin/postmap -F hash:/run/keys/postfix-sni
39 networksStyle = "host";
40 hostname ="${networking.hostName}.${networking.domain}";
41 domain = networking.domain;
42 origin = "$myhostname";
45 "localhost.localdomain"
48 postmasterAlias = "root";
49 rootAlias = "root@${networking.domain}";
50 sslKey = "/var/lib/acme/${networking.domain}/key.pem";
51 sslCert = "/var/lib/acme/${networking.domain}/fullchain.pem";
57 # Parse the extension in email address, eg. contact+extension@
58 recipientDelimiter = "+";
60 debug_peer_level = "4";
62 #"chomsky.autogeree.net"
64 #"mail.sourcephile.fr"
68 # Sending to the world
70 # Appending .domain is the MUA's job
71 append_dot_mydomain = false;
72 smtp_body_checks = "";
73 #smtp_cname_overrides_servername = false;
74 smtp_connect_timeout = "60s";
75 #smtp_header_checks = "regexp:/var/lib/postfix/smtp_header_checks";
76 smtp_mime_header_checks = "";
77 smtp_nested_header_checks = "";
78 smtp_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
79 #smtp_tls_fingerprint_digest = "sha1";
80 smtp_tls_loglevel = "1";
81 #smtp_tls_note_starttls_offer = true;
82 #smtp_tls_policy_maps = "hash:/var/lib/postfix/conf/tls_policy";
83 # Only allow TLSv* protocols
84 smtp_tls_protocols = [ "!SSLv2" "!SSLv3" ];
85 #smtp_tls_scert_verifydepth = "5";
86 #smtp_tls_secure_cert_match = [ "nexthop" "dot-nexthop" ];
87 smtp_tls_security_level = "may";
88 smtp_tls_session_cache_database = "btree:$data_directory/smtp_tls_session_cache";
89 #smtp_tls_session_cache_timeout = "3600s";
90 #smtp_tls_verify_cert_match = "hostname";
93 # Receiving from the world
95 message_size_limit = "20480000";
96 maximal_queue_lifetime = "5d";
97 default_extra_recipient_limit = "5000";
98 line_length_limit = "2048";
99 duplicate_filter_limit = "5000";
100 # Stops mail from poorly written software
101 strict_rfc821_envelopes = true;
102 mime_header_checks = [];
103 milter_header_checks = [];
104 nested_header_checks = [];
107 permit_mx_backup_networks = [];
108 propagate_unmatched_extensions = [ "canonical" "virtual" "alias" ];
109 #masquerade_classes = [ "envelope_sender" "header_sender" "header_recipient" ];
110 #masquerade_domains = "";
111 #masquerade_exceptions = "root";
113 # Stops some techniques used to harvest email addresses
114 disable_vrfy_command = true;
115 enable_long_queue_ids = false;
116 # Useful to test restrictions
117 smtpd_authorized_xclient_hosts = "127.0.0.1";
118 smtpd_banner = "$myhostname ESMTP $mail_name (NixOS)";
119 smtpd_client_connection_count_limit = "50";
120 smtpd_client_connection_rate_limit = "0";
121 smtpd_client_event_limit_exceptions = "$mynetworks";
122 smtpd_client_message_rate_limit = "0";
123 smtpd_client_new_tls_session_rate_limit = "0";
124 smtpd_client_port_logging = false;
125 smtpd_client_recipient_rate_limit = "0";
127 smtpd_error_sleep_time = "5";
128 # Needed to enforce reject_unknown_helo_hostname
129 smtpd_helo_required = true;
130 smtpd_helo_restrictions = [
131 "reject_invalid_helo_hostname"
132 "reject_non_fqdn_helo_hostname"
133 # Don't talk to mail systems that don't know their own hostname.
134 "reject_unknown_helo_hostname"
137 smtpd_client_restrictions = [
139 # Set in postfix/*.nix and used in submissions/smptd
140 # with reject_sender_login_mismatch
141 smtpd_sender_login_maps = [];
142 smtpd_sender_restrictions = [
143 "reject_non_fqdn_sender"
146 smtpd_reject_unlisted_recipient = true;
147 # Check the RCPT TO, before smtpd_recipient_restrictions
148 # Restrictions based on what is allowed or not,
149 # these are applied before smtpd_recipient_restrictions
150 smtpd_relay_restrictions = [
152 # Check the recipient's address in virtual_mailbox_domains and virtual_mailbox_maps
153 "permit_auth_destination"
154 # The world is only authorized to use our relay for the above destinations.
157 # Restrictions based on what is working or not
158 smtpd_recipient_restrictions = [
159 # Reject if the domain is not fully qualified
160 "reject_non_fqdn_recipient"
161 # Reject if the domain is not working, even before bothering to check the address
162 "reject_unknown_recipient_domain"
163 # Reject if the address is not working
164 # WARNING: this does not work if the recipient is greylisting.
165 # WARNING: verify(8) has a cache, dumpable if verify(8) is stopped, with:
166 # postmap -s btree:/var/lib/postfix/data/verify_cache
167 #"reject_unverified_recipient"
170 # Trust the verify database
171 #unverified_recipient_reject_code = "550";
172 smtpd_data_restrictions = [
173 # Force the smtpd's client to wait OK before sending
174 "reject_unauth_pipelining"
177 smtpd_end_of_data_restrictions = [
178 # Enforce mail volume quota via policy service callouts.
179 #check_policy_service unix:private/policy
182 smtpd_peername_lookup = true;
183 smtpd_recipient_limit = "5000";
184 smtpd_recipient_overshoot_limit = "5000";
185 #smtpd_restriction_classes = "";
186 #smtpd_sasl_auth_enable = true;
187 #smtpd_sasl_path = "private/auth";
188 #smtpd_sasl_security_options = "noanonymous";
189 #smtpd_sasl_type = "dovecot";
190 smtpd_starttls_timeout = "300s";
191 #smtpd_tls_always_issue_session_ids = true;
192 #smtpd_tls_CApath = "/etc/postfix/x509/ca/";
193 smtpd_tls_ask_ccert = false;
194 #smtpd_tls_ccert_verifydepth = "5";
195 smtpd_tls_ciphers = "high";
196 smtpd_tls_eecdh_grade = "auto";
197 # Disable weak ciphers as reported by https://ssl-tools.net
198 # https://serverfault.com/questions/744168/how-to-disable-rc4-on-postfix
199 smtpd_tls_exclude_ciphers = [ "ADH" "MD5" "CAMELLIA" "SEED" "3DES" "DES" "RC4" "eNULL" "aNULL" ];
200 smtpd_tls_fingerprint_digest = "sha512";
201 # Log only a summary message on TLS handshake completion
202 smtpd_tls_loglevel = "1";
203 smtpd_tls_mandatory_ciphers = "high";
204 smtpd_tls_mandatory_protocols = [ "!SSLv2" "!SSLv3" ];
206 smtpd_tls_protocols = [ "!SSLv2" "!SSLv3" ];
207 #smtpd_tls_received_header = false;
208 smtpd_tls_req_ccert = false;
209 # Postfix 2.3 and later
211 # Mandatory TLS encryption: announce STARTTLS support to SMTP clients, and require that clients use TLS
212 # encryption. According to [1720]RFC 2487 this MUST NOT be applied in case of a publicly-referenced
213 # SMTP server. Instead, this option should be used only on dedicated servers.
214 smtpd_tls_security_level = "may";
215 smtpd_tls_session_cache_database = "btree:$data_directory/smtpd_tls_session_cache";
216 #smtpd_tls_session_cache_timeout = "3600s";
217 #smtpd_tls_chain_files =
220 #relay_clientcerts = hash:/var/lib/postfix/conf/relay_clientcerts
221 # This is where to put backup MX domains
223 relay_recipient_maps = [];
225 # Use a non blocking source of randomness
226 tls_random_source = "dev:/dev/urandom";
227 # Map each domain to a specific X.509 certificate
228 tls_server_sni_maps = "hash:/run/keys/postfix-sni";
230 # Only explicitely aliased accounts have a mail, not all the passwd
231 local_recipient_maps = "$alias_maps";
232 # Note that the local transport rewrites the envelope recipient
233 # according to the alias_maps, and thus the aliasing is transparent
234 # to the nexthop (eg. dovecot)
235 #local_transport = local:$myhostname
236 # No console bell on new mail
240 "$home/.forward''${recipient_delimiter}''${extension}"
245 # Filled by the postfix/*.nix
246 virtual_mailbox_domains = [];
247 # Completed by the postfix/*.nix
248 virtual_mailbox_maps = [
249 "hash:/etc/postfix/virtual"
251 virtual_transport = "lmtp:unix:private/dovecot-lmtp";
253 dovecot_destination_recipient_limit = "1";
254 virtual_transport = "dovecot";
257 # There is no fallback
258 fallback_transport = "";
260 virtualMapType = "hash";
265 then concatStringsSep "," value
267 if value == true then "yes"
268 else if value == false then "no"
270 mkKeyVal = opt: val: [ "-o" (opt + "=" + mkVal val) ];
271 mkArgs = args: lib.concatLists (lib.mapAttrsToList mkKeyVal args);
275 cleanup_service_name = "submissions-header-cleanup";
278 # Implicit TLS on port 465
279 # https://tools.ietf.org/html/rfc8314#section-3.3
285 syslog_name = "postfix/submissions";
286 # Implicit TLS, not STARTTLS
287 smtpd_tls_wrappermode = true;
288 smtpd_tls_mandatory_protocols = [
290 # K-9 Mail 5.600 still requires this..
293 milter_macro_daemon_name = "ORIGINATING";
294 smtpd_helo_restrictions = [
295 "permit_sasl_authenticated"
296 ] ++ postfix.config.smtpd_helo_restrictions;
297 smtpd_relay_restrictions = [
298 # SASL authorizes to send to the world
299 "permit_sasl_authenticated"
302 smtpd_sasl_auth_enable = true;
303 smtpd_sasl_type = "dovecot";
304 smtpd_sasl_path = "private/auth";
305 smtpd_sasl_local_domain = "";
306 # Offer SASL authentication only after a TLS-encrypted session has been established
307 smtpd_tls_auth_only = true;
308 smtpd_sasl_tls_security_options = [ "noanonymous" ];
309 # Do not put SASL logins in mail headers
310 smtpd_sasl_authenticated_header = false;
311 # Who cares about (old) Outlook
312 broken_sasl_auth_clients = false;
313 smtpd_sender_restrictions = [
314 "reject_non_fqdn_sender"
315 # Check that the SASL user is using only its own
316 # mail addresses on the envelope, as indicated in smtpd_sender_login_maps
317 "reject_sender_login_mismatch"
320 # No X.509 certificates for users, for now
321 smtpd_tls_ask_ccert = false;
322 smtpd_tls_ccert_verifydepth = 0;
323 smtpd_tls_loglevel = 1;
324 smtpd_tls_req_ccert = false;
325 cleanup_service_name = "submissions-header-cleanup";
328 submissions-header-cleanup = {
334 header_checks = "pcre:" + pkgs.writeText "submission_header_cleanup_rules" ''
335 # Removes sensitive headers from mails handed in via the submission or smtps port.
336 # See https://thomas-leister.de/mailserver-debian-stretch/
337 # Uses "pcre" style regex.
340 /^User-Agent:/ IGNORE
341 /^X-Enigmail:/ IGNORE
343 /^X-Originating-IP:/ IGNORE
354 # rspamd could be used as a milter, but then it cannot apply
355 # its checks "per user" (milter is not yet dispatched to
356 # users), so we wrap dovecot-lda inside rspamc per recipient
358 rspamc_dovecot = pkgs.writeScriptBin "rspamc_dovecot" ''
359 #!${pkgs.stdenv.shell}
361 original_recipient="$2"
363 ${pkgs.coreutils}/bin/cat - | \
364 (${pkgs.rspamd}/bin/rspamc -h ${config.myServices.mail.rspamd.sockets.worker-controller} -c bayes -d "$user" --mime || true) | \
365 ${pkgs.dovecot}/libexec/dovecot/dovecot-lda -f "$sender" -a "$original_recipient" -d "$user"
368 "flags=DRhu" "user=vhost:vhost"
369 "argv=${rspamc_dovecot}/bin/rspamc_dovecot \${sender} \${original_recipient} \${user}@\${nexthop}"
375 #spfcheck unix - n n - 0 spawn
376 # user=policyd-spf argv=/usr/sbin/postfix-policyd-spf-perl
377 # -o smtpd_sender_restrictions=reject_sender_login_mismatch
378 # -o smtpd_sender_login_maps=hash:/etc/postfix/vaccounts
379 # -o cleanup_service_name=submissions-header-cleanup
380 #spfcheck unix - n n - 0 spawn
381 # user=policyd-spf argv=/usr/bin/postfix-policyd-spf-perl
382 #uucp unix - n n - - pipe
383 # flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
384 #smtp inet n - - - - smtpd
385 # -o cleanup_service_name=pre-cleanup
386 # -o content_filter=amavis:[127.0.0.1]:10024
387 # -o smtpd_sender_restrictions=reject_unauth_pipelining,reject_non_fqdn_sender,permit
388 # -o receive_override_options=no_address_mappings
389 #amavis unix - - n - 2 lmtp
390 # -o lmtp_data_done_timeout=1200
391 # -o lmtp_send_xforward_command=yes
392 # -o lmtp_tls_note_starttls_offer=no
393 #127.0.0.1:10025 inet n - n - - smtpd
395 # -o local_header_rewrite_clients=
396 # -o local_recipient_maps=
397 # -o mynetworks=127.0.0.0/8
398 # -o receive_override_options=no_header_body_checks,no_milters,no_unknown_recipient_checks
399 # -o relay_recipient_maps=
400 # -o smtpd_client_connection_count_limit=0
401 # -o smtpd_client_connection_rate_limit=0
402 # -o smtpd_client_restrictions=permit_mynetworks,reject
403 # -o smtpd_data_restrictions=reject_unauth_pipelining
404 # -o smtpd_delay_reject=no
405 # -o smtpd_end_of_data_restrictions=
406 # -o smtpd_error_sleep_time=0
407 # -o smtpd_hard_error_limit=1000
408 # -o smtpd_helo_restrictions=
410 # -o smtpd_recipient_restrictions=permit_mynetworks,reject
411 # -o smtpd_restriction_classes=
412 # -o smtpd_sender_restrictions=
413 # -o smtpd_soft_error_limit=1001
414 # -o strict_rfc821_envelopes=yes
415 #submission inet n - - - - smtpd
416 # -o cleanup_service_name=pre-cleanup
417 # -o content_filter=amavis:[127.0.0.1]:10024
418 # -o milter_macro_daemon_name=ORIGINATING
419 # -o receive_override_options=no_address_mappings
420 # -o smtpd_sender_restrictions=permit_tls_clientcerts,reject
421 # -o smtpd_tls_ask_ccert=yes
422 # -o smtpd_tls_auth_only=yes
423 # -o smtpd_tls_ccert_verifydepth=2
424 # -o smtpd_tls_loglevel=1
425 # -o smtpd_tls_req_ccert=yes
426 # -o smtpd_tls_security_level=encrypt
427 #smtps inet n - - - - smtpd
428 # -o milter_macro_daemon_name=ORIGINATING
429 # -o smtpd_client_restrictions=permit_sasl_authenticated,reject
430 # -o smtpd_sasl_auth_enable=yes
431 # -o smtpd_tls_ask_ccert=yes
432 # -o smtpd_tls_auth_only=yes
433 # -o smtpd_tls_ccert_verifydepth=0
434 # -o smtpd_tls_loglevel=1
435 # -o smtpd_tls_req_ccert=no
436 # -o smtpd_tls_security_level=encrypt
437 # -o smtpd_tls_wrappermode=yes
438 #pickup fifo n - - 60 1 pickup
439 # -o cleanup_service_name=pre-cleanup
440 # -o content_filter=amavis:[127.0.0.1]:10024
441 #pre-cleanup unix n - - - 0 cleanup
442 # -o virtual_alias_maps=
443 #cleanup unix n - - - 0 cleanup
444 # -o mime_header_checks=
445 # -o nested_header_checks=
449 #sympa unix - n n - - pipe
450 # flags=R user=sympa argv=/usr/lib/sympa/bin/queue ''${recipient}
451 #sympabounce unix - n n - - pipe
452 # flags=R user=sympa argv=/usr/lib/sympa/bin/bouncequeue ''${recipient}
455 #noclue unix - n n - - pipe
456 # flags=q user=noclue argv=/usr/local/bin/noclue-delivery ${recipient} ${sender}