diff options
Diffstat (limited to 'hosts/surtr/email/default.nix')
| -rw-r--r-- | hosts/surtr/email/default.nix | 105 |
1 files changed, 85 insertions, 20 deletions
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 845f6455..c6253e4c 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 | ||
| 3 | with lib; | 3 | with 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,6 +206,7 @@ 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"; |
| @@ -251,7 +268,7 @@ in { | |||
| 251 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 268 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
| 252 | smtputf8_enable = false; | 269 | smtputf8_enable = false; |
| 253 | 270 | ||
| 254 | authorized_submit_users = "inline:{ root= postfwd= }"; | 271 | authorized_submit_users = "inline:{ root= postfwd= dovecot2= }"; |
| 255 | 272 | ||
| 256 | postscreen_access_list = ""; | 273 | postscreen_access_list = ""; |
| 257 | postscreen_denylist_action = "drop"; | 274 | postscreen_denylist_action = "drop"; |
| @@ -280,7 +297,7 @@ in { | |||
| 280 | hosts = postgresql:///email | 297 | hosts = postgresql:///email |
| 281 | dbname = email | 298 | dbname = email |
| 282 | 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')) | 299 | 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')) |
| 283 | ''},permit_tls_all_clientcerts,reject}'' | 300 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
| 284 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 301 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
| 285 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 302 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 286 | "-o" "unverified_sender_reject_code=550" | 303 | "-o" "unverified_sender_reject_code=550" |
| @@ -310,7 +327,7 @@ in { | |||
| 310 | hosts = postgresql:///email | 327 | hosts = postgresql:///email |
| 311 | dbname = email | 328 | dbname = email |
| 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')) | 329 | 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')) |
| 313 | ''},permit_sasl_authenticated,reject}'' | 330 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
| 314 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 331 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
| 315 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 332 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 316 | "-o" "unverified_sender_reject_code=550" | 333 | "-o" "unverified_sender_reject_code=550" |
| @@ -672,7 +689,7 @@ in { | |||
| 672 | plugin { | 689 | plugin { |
| 673 | plugin = fts fts_xapian | 690 | plugin = fts fts_xapian |
| 674 | fts = xapian | 691 | fts = xapian |
| 675 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 692 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
| 676 | 693 | ||
| 677 | fts_autoindex = yes | 694 | fts_autoindex = yes |
| 678 | 695 | ||
| @@ -692,7 +709,7 @@ in { | |||
| 692 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 709 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
| 693 | serviceConfig = { | 710 | serviceConfig = { |
| 694 | Type = "oneshot"; | 711 | Type = "oneshot"; |
| 695 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 712 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
| 696 | PrivateDevices = true; | 713 | PrivateDevices = true; |
| 697 | PrivateNetwork = true; | 714 | PrivateNetwork = true; |
| 698 | ProtectKernelTunables = true; | 715 | ProtectKernelTunables = true; |
| @@ -777,7 +794,7 @@ in { | |||
| 777 | systemd.services.dovecot2 = { | 794 | systemd.services.dovecot2 = { |
| 778 | preStart = '' | 795 | preStart = '' |
| 779 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 796 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
| 780 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 797 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
| 781 | done | 798 | done |
| 782 | ''; | 799 | ''; |
| 783 | 800 | ||
| @@ -844,15 +861,16 @@ in { | |||
| 844 | charset utf-8; | 861 | charset utf-8; |
| 845 | source_charset utf-8; | 862 | source_charset utf-8; |
| 846 | ''; | 863 | ''; |
| 847 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 864 | root = pkgs.writeTextFile { |
| 848 | mkdir -p $out/.well-known | 865 | name = "mta-sts.${domain}"; |
| 849 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 866 | destination = "/.well-known/mta-sts.txt"; |
| 867 | text = '' | ||
| 850 | version: STSv1 | 868 | version: STSv1 |
| 851 | mode: enforce | 869 | mode: enforce |
| 852 | max_age: 2419200 | 870 | max_age: 2419200 |
| 853 | mx: mailin.${domain} | 871 | mx: mailin.${domain} |
| 854 | ''} $out/.well-known/mta-sts.txt | 872 | ''; |
| 855 | ''; | 873 | }; |
| 856 | }; | 874 | }; |
| 857 | }) emailDomains); | 875 | }) emailDomains); |
| 858 | }; | 876 | }; |
| @@ -869,7 +887,7 @@ in { | |||
| 869 | systemd.services.spm = { | 887 | systemd.services.spm = { |
| 870 | serviceConfig = { | 888 | serviceConfig = { |
| 871 | Type = "notify"; | 889 | Type = "notify"; |
| 872 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 890 | ExecStart = getExe' pkgs.spm "spm-server"; |
| 873 | User = "spm"; | 891 | User = "spm"; |
| 874 | Group = "spm"; | 892 | Group = "spm"; |
| 875 | 893 | ||
| @@ -927,7 +945,7 @@ in { | |||
| 927 | serviceConfig = { | 945 | serviceConfig = { |
| 928 | Type = "notify"; | 946 | Type = "notify"; |
| 929 | 947 | ||
| 930 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 948 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
| 931 | 949 | ||
| 932 | Environment = [ | 950 | Environment = [ |
| 933 | "PGDATABASE=email" | 951 | "PGDATABASE=email" |
| @@ -960,6 +978,53 @@ in { | |||
| 960 | }; | 978 | }; |
| 961 | users.groups."postfix-ccert-sender-policy" = {}; | 979 | users.groups."postfix-ccert-sender-policy" = {}; |
| 962 | 980 | ||
| 981 | systemd.sockets."postfix-internal-policy" = { | ||
| 982 | requiredBy = ["postfix.service"]; | ||
| 983 | wants = ["postfix-internal-policy.service"]; | ||
| 984 | socketConfig = { | ||
| 985 | ListenStream = "/run/postfix-internal-policy.sock"; | ||
| 986 | }; | ||
| 987 | }; | ||
| 988 | systemd.services."postfix-internal-policy" = { | ||
| 989 | after = [ "postgresql.service" ]; | ||
| 990 | bindsTo = [ "postgresql.service" ]; | ||
| 991 | |||
| 992 | serviceConfig = { | ||
| 993 | Type = "notify"; | ||
| 994 | |||
| 995 | ExecStart = lib.getExe internal-policy-server; | ||
| 996 | |||
| 997 | Environment = [ | ||
| 998 | "PGDATABASE=email" | ||
| 999 | ]; | ||
| 1000 | |||
| 1001 | DynamicUser = false; | ||
| 1002 | User = "postfix-internal-policy"; | ||
| 1003 | Group = "postfix-internal-policy"; | ||
| 1004 | ProtectSystem = "strict"; | ||
| 1005 | SystemCallFilter = "@system-service"; | ||
| 1006 | NoNewPrivileges = true; | ||
| 1007 | ProtectKernelTunables = true; | ||
| 1008 | ProtectKernelModules = true; | ||
| 1009 | ProtectKernelLogs = true; | ||
| 1010 | ProtectControlGroups = true; | ||
| 1011 | MemoryDenyWriteExecute = true; | ||
| 1012 | RestrictSUIDSGID = true; | ||
| 1013 | KeyringMode = "private"; | ||
| 1014 | ProtectClock = true; | ||
| 1015 | RestrictRealtime = true; | ||
| 1016 | PrivateDevices = true; | ||
| 1017 | PrivateTmp = true; | ||
| 1018 | ProtectHostname = true; | ||
| 1019 | ReadWritePaths = ["/run/postgresql"]; | ||
| 1020 | }; | ||
| 1021 | }; | ||
| 1022 | users.users."postfix-internal-policy" = { | ||
| 1023 | isSystemUser = true; | ||
| 1024 | group = "postfix-internal-policy"; | ||
| 1025 | }; | ||
| 1026 | users.groups."postfix-internal-policy" = {}; | ||
| 1027 | |||
| 963 | services.postfwd = { | 1028 | services.postfwd = { |
| 964 | enable = true; | 1029 | enable = true; |
| 965 | cache = false; | 1030 | cache = false; |
