summaryrefslogtreecommitdiff
path: root/hosts/surtr/email/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/surtr/email/default.nix')
-rw-r--r--hosts/surtr/email/default.nix145
1 files changed, 111 insertions, 34 deletions
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix
index 4196a8bc..c993bb18 100644
--- a/hosts/surtr/email/default.nix
+++ b/hosts/surtr/email/default.nix
@@ -1,4 +1,4 @@
1{ config, pkgs, lib, flakeInputs, ... }: 1{ config, pkgs, lib, flake, flakeInputs, ... }:
2 2
3with lib; 3with lib;
4 4
@@ -15,7 +15,7 @@ let
15 15
16 for file in $out/pipe/bin/*; do 16 for file in $out/pipe/bin/*; do
17 wrapProgram $file \ 17 wrapProgram $file \
18 --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" 18 --set PATH "${makeBinPath (with pkgs; [coreutils rspamd])}"
19 done 19 done
20 ''; 20 '';
21 }; 21 };
@@ -33,12 +33,28 @@ let
33 }); 33 });
34 }); 34 });
35 }; 35 };
36 internal-policy-server =
37 let
38 workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./internal-policy-server; };
39 pythonSet = flake.lib.pythonSet {
40 inherit pkgs;
41 python = pkgs.python312;
42 overlay = workspace.mkPyprojectOverlay {
43 sourcePreference = "wheel";
44 };
45 };
46 virtualEnv = pythonSet.mkVirtualEnv "internal-policy-server-env" workspace.deps.default;
47 in virtualEnv.overrideAttrs (oldAttrs: {
48 meta = (oldAttrs.meta or {}) // {
49 mainProgram = "internal-policy-server";
50 };
51 });
36 52
37 nftables-nologin-script = pkgs.writeScript "nftables-mail-nologin" '' 53 nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" {
38 #!${pkgs.zsh}/bin/zsh 54 inputs = with pkgs; [inetutils nftables gnugrep findutils];
39 55 interpreter = lib.getExe pkgs.zsh;
56 } ''
40 set -e 57 set -e
41 export PATH="${lib.makeBinPath (with pkgs; [inetutils nftables])}:$PATH"
42 58
43 typeset -a as_sets mnt_bys route route6 59 typeset -a as_sets mnt_bys route route6
44 as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) 60 as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets})
@@ -51,7 +67,7 @@ let
51 elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then 67 elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then
52 route6+=($match[1]) 68 route6+=($match[1])
53 fi 69 fi
54 done < <(whois -h whois.radb.net "!i''${as_set},1" | egrep -o 'AS[0-9]+' | xargs -- whois -h whois.radb.net -- -i origin) 70 done < <(whois -h whois.radb.net "!i''${as_set},1" | grep -Eo 'AS[0-9]+' | xargs whois -h whois.radb.net -- -i origin)
55 done 71 done
56 for mnt_by in $mnt_bys; do 72 for mnt_by in $mnt_bys; do
57 while IFS=$'\n' read line; do 73 while IFS=$'\n' read line; do
@@ -190,16 +206,19 @@ in {
190 "reject_unauth_destination" 206 "reject_unauth_destination"
191 "reject_unknown_recipient_domain" 207 "reject_unknown_recipient_domain"
192 "reject_unverified_recipient" 208 "reject_unverified_recipient"
209 "check_policy_service unix:/run/postfix-internal-policy.sock"
193 ]; 210 ];
194 unverified_recipient_reject_code = "550"; 211 unverified_recipient_reject_code = "550";
195 unverified_recipient_reject_reason = "Recipient address lookup failed"; 212 unverified_recipient_reject_reason = "Recipient address lookup failed";
196 address_verify_map = "internal:address_verify_map"; 213 address_verify_map = "internal:address_verify_map";
197 address_verify_positive_expire_time = "1h"; 214 address_verify_positive_expire_time = "1h";
198 address_verify_positive_refresh_time = "15m"; 215 address_verify_positive_refresh_time = "15m";
199 address_verify_negative_expire_time = "15s"; 216 address_verify_negative_expire_time = "5m";
200 address_verify_negative_refresh_time = "5s"; 217 address_verify_negative_refresh_time = "1m";
201 address_verify_cache_cleanup_interval = "5s"; 218 address_verify_cache_cleanup_interval = "12h";
219 address_verify_poll_count = "\${stress?15}\${stress:30}";
202 address_verify_poll_delay = "1s"; 220 address_verify_poll_delay = "1s";
221 address_verify_sender_ttl = "30045s";
203 222
204 smtpd_relay_restrictions = [ 223 smtpd_relay_restrictions = [
205 "check_ccert_access ${relay_ccert}" 224 "check_ccert_access ${relay_ccert}"
@@ -213,7 +232,7 @@ in {
213 smtpd_client_event_limit_exceptions = ""; 232 smtpd_client_event_limit_exceptions = "";
214 233
215 milter_default_action = "accept"; 234 milter_default_action = "accept";
216 smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; 235 smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock" "local:/run/postsrsd/postsrsd-milter.sock"];
217 non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; 236 non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"];
218 237
219 alias_maps = ""; 238 alias_maps = "";
@@ -235,11 +254,6 @@ in {
235 ::/0 silent-discard, dsn 254 ::/0 silent-discard, dsn
236 ''}"; 255 ''}";
237 256
238 sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}";
239 sender_canonical_classes = "envelope_sender";
240 recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}";
241 recipient_canonical_classes = ["envelope_recipient" "header_recipient"];
242
243 virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' 257 virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" ''
244 hosts = postgresql:///email 258 hosts = postgresql:///email
245 dbname = email 259 dbname = email
@@ -254,11 +268,24 @@ in {
254 virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; 268 virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp";
255 smtputf8_enable = false; 269 smtputf8_enable = false;
256 270
257 authorized_submit_users = "inline:{ root= postfwd= }"; 271 authorized_submit_users = "inline:{ root= postfwd= dovecot2= }";
272 authorized_flush_users = "inline:{ root= }";
273 authorized_mailq_users = "inline:{ root= }";
258 274
259 postscreen_access_list = ""; 275 postscreen_access_list = "";
260 postscreen_denylist_action = "drop"; 276 postscreen_denylist_action = "drop";
261 postscreen_greet_action = "enforce"; 277 postscreen_greet_action = "enforce";
278
279 sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" ''
280 hosts = postgresql:///email
281 dbname = email
282 query = SELECT value FROM sender_bcc_maps WHERE key = '%s'
283 ''}'';
284 recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" ''
285 hosts = postgresql:///email
286 dbname = email
287 query = SELECT value FROM recipient_bcc_maps WHERE key = '%s'
288 ''}'';
262 }; 289 };
263 masterConfig = { 290 masterConfig = {
264 "465" = { 291 "465" = {
@@ -283,7 +310,7 @@ in {
283 hosts = postgresql:///email 310 hosts = postgresql:///email
284 dbname = email 311 dbname = email
285 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) 312 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s'))
286 ''},permit_tls_all_clientcerts,reject}'' 313 ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}''
287 "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" 314 "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject"
288 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" 315 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
289 "-o" "unverified_sender_reject_code=550" 316 "-o" "unverified_sender_reject_code=550"
@@ -313,7 +340,7 @@ in {
313 hosts = postgresql:///email 340 hosts = postgresql:///email
314 dbname = email 341 dbname = email
315 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) 342 query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s'))
316 ''},permit_sasl_authenticated,reject}'' 343 ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}''
317 "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" 344 "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject"
318 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" 345 "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}"
319 "-o" "unverified_sender_reject_code=550" 346 "-o" "unverified_sender_reject_code=550"
@@ -364,17 +391,19 @@ in {
364 391
365 services.postsrsd = { 392 services.postsrsd = {
366 enable = true; 393 enable = true;
367 domain = "surtr.yggdrasil.li"; 394 domains = [ "surtr.yggdrasil.li" ] ++ concatMap (domain: [".${domain}" domain]) emailDomains;
368 separator = "+"; 395 separator = "+";
369 excludeDomains = [ "surtr.yggdrasil.li" 396 extraConfig = ''
370 ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; 397 socketmap = unix:/run/postsrsd/postsrsd-socketmap.sock
398 milter = unix:/run/postsrsd/postsrsd-milter.sock
399 '';
371 }; 400 };
372 401
373 services.opendkim = { 402 services.opendkim = {
374 enable = true; 403 enable = true;
375 user = "postfix"; group = "postfix"; 404 user = "postfix"; group = "postfix";
376 socket = "local:/run/opendkim/opendkim.sock"; 405 socket = "local:/run/opendkim/opendkim.sock";
377 domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; 406 domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}'';
378 selector = "surtr"; 407 selector = "surtr";
379 configFile = builtins.toFile "opendkim.conf" '' 408 configFile = builtins.toFile "opendkim.conf" ''
380 Syslog true 409 Syslog true
@@ -484,6 +513,7 @@ in {
484 513
485 users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; 514 users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ];
486 515
516 environment.systemPackages = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ];
487 services.dovecot2 = { 517 services.dovecot2 = {
488 enable = true; 518 enable = true;
489 enablePAM = false; 519 enablePAM = false;
@@ -491,7 +521,6 @@ in {
491 sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; 521 sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem";
492 sslCACert = toString ./ca/ca.crt; 522 sslCACert = toString ./ca/ca.crt;
493 mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; 523 mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u";
494 modules = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ];
495 mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; 524 mailPlugins.globally.enable = [ "fts" "fts_xapian" ];
496 protocols = [ "lmtp" "sieve" ]; 525 protocols = [ "lmtp" "sieve" ];
497 sieve = { 526 sieve = {
@@ -673,7 +702,7 @@ in {
673 plugin { 702 plugin {
674 plugin = fts fts_xapian 703 plugin = fts fts_xapian
675 fts = xapian 704 fts = xapian
676 fts_xapian = partial=2 full=20 attachments=1 verbose=1 705 fts_xapian = partial=3 full=20 attachments=1 verbose=1
677 706
678 fts_autoindex = yes 707 fts_autoindex = yes
679 708
@@ -693,7 +722,7 @@ in {
693 startAt = "*-*-* 22:00:00 Europe/Berlin"; 722 startAt = "*-*-* 22:00:00 Europe/Berlin";
694 serviceConfig = { 723 serviceConfig = {
695 Type = "oneshot"; 724 Type = "oneshot";
696 ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; 725 ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A";
697 PrivateDevices = true; 726 PrivateDevices = true;
698 PrivateNetwork = true; 727 PrivateNetwork = true;
699 ProtectKernelTunables = true; 728 ProtectKernelTunables = true;
@@ -778,7 +807,7 @@ in {
778 systemd.services.dovecot2 = { 807 systemd.services.dovecot2 = {
779 preStart = '' 808 preStart = ''
780 for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do 809 for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do
781 ${pkgs.dovecot_pigeonhole}/bin/sievec $f 810 ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f
782 done 811 done
783 ''; 812 '';
784 813
@@ -845,15 +874,16 @@ in {
845 charset utf-8; 874 charset utf-8;
846 source_charset utf-8; 875 source_charset utf-8;
847 ''; 876 '';
848 root = pkgs.runCommand "mta-sts.${domain}" {} '' 877 root = pkgs.writeTextFile {
849 mkdir -p $out/.well-known 878 name = "mta-sts.${domain}";
850 cp ${pkgs.writeText "mta-sts.${domain}.txt" '' 879 destination = "/.well-known/mta-sts.txt";
880 text = ''
851 version: STSv1 881 version: STSv1
852 mode: enforce 882 mode: enforce
853 max_age: 2419200 883 max_age: 2419200
854 mx: mailin.${domain} 884 mx: mailin.${domain}
855 ''} $out/.well-known/mta-sts.txt 885 '';
856 ''; 886 };
857 }; 887 };
858 }) emailDomains); 888 }) emailDomains);
859 }; 889 };
@@ -870,7 +900,7 @@ in {
870 systemd.services.spm = { 900 systemd.services.spm = {
871 serviceConfig = { 901 serviceConfig = {
872 Type = "notify"; 902 Type = "notify";
873 ExecStart = "${pkgs.spm}/bin/spm-server"; 903 ExecStart = getExe' pkgs.spm "spm-server";
874 User = "spm"; 904 User = "spm";
875 Group = "spm"; 905 Group = "spm";
876 906
@@ -928,7 +958,7 @@ in {
928 serviceConfig = { 958 serviceConfig = {
929 Type = "notify"; 959 Type = "notify";
930 960
931 ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; 961 ExecStart = getExe' ccert-policy-server "ccert-policy-server";
932 962
933 Environment = [ 963 Environment = [
934 "PGDATABASE=email" 964 "PGDATABASE=email"
@@ -961,6 +991,53 @@ in {
961 }; 991 };
962 users.groups."postfix-ccert-sender-policy" = {}; 992 users.groups."postfix-ccert-sender-policy" = {};
963 993
994 systemd.sockets."postfix-internal-policy" = {
995 requiredBy = ["postfix.service"];
996 wants = ["postfix-internal-policy.service"];
997 socketConfig = {
998 ListenStream = "/run/postfix-internal-policy.sock";
999 };
1000 };
1001 systemd.services."postfix-internal-policy" = {
1002 after = [ "postgresql.service" ];
1003 bindsTo = [ "postgresql.service" ];
1004
1005 serviceConfig = {
1006 Type = "notify";
1007
1008 ExecStart = lib.getExe internal-policy-server;
1009
1010 Environment = [
1011 "PGDATABASE=email"
1012 ];
1013
1014 DynamicUser = false;
1015 User = "postfix-internal-policy";
1016 Group = "postfix-internal-policy";
1017 ProtectSystem = "strict";
1018 SystemCallFilter = "@system-service";
1019 NoNewPrivileges = true;
1020 ProtectKernelTunables = true;
1021 ProtectKernelModules = true;
1022 ProtectKernelLogs = true;
1023 ProtectControlGroups = true;
1024 MemoryDenyWriteExecute = true;
1025 RestrictSUIDSGID = true;
1026 KeyringMode = "private";
1027 ProtectClock = true;
1028 RestrictRealtime = true;
1029 PrivateDevices = true;
1030 PrivateTmp = true;
1031 ProtectHostname = true;
1032 ReadWritePaths = ["/run/postgresql"];
1033 };
1034 };
1035 users.users."postfix-internal-policy" = {
1036 isSystemUser = true;
1037 group = "postfix-internal-policy";
1038 };
1039 users.groups."postfix-internal-policy" = {};
1040
964 services.postfwd = { 1041 services.postfwd = {
965 enable = true; 1042 enable = true;
966 cache = false; 1043 cache = false;