diff options
Diffstat (limited to 'hosts/surtr/email/default.nix')
| -rw-r--r-- | hosts/surtr/email/default.nix | 108 |
1 files changed, 106 insertions, 2 deletions
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 83820c65..d9e6fff9 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix | |||
| @@ -52,6 +52,22 @@ let | |||
| 52 | mainProgram = "internal-policy-server"; | 52 | mainProgram = "internal-policy-server"; |
| 53 | }; | 53 | }; |
| 54 | }); | 54 | }); |
| 55 | password-server = | ||
| 56 | let | ||
| 57 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./password-server; }; | ||
| 58 | pythonSet = flake.lib.pythonSet { | ||
| 59 | inherit pkgs; | ||
| 60 | python = pkgs.python3; | ||
| 61 | overlay = workspace.mkPyprojectOverlay { | ||
| 62 | sourcePreference = "wheel"; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | virtualEnv = pythonSet.mkVirtualEnv "password-server-env" workspace.deps.default; | ||
| 66 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
| 67 | meta = (oldAttrs.meta or {}) // { | ||
| 68 | mainProgram = "password-server"; | ||
| 69 | }; | ||
| 70 | }); | ||
| 55 | 71 | ||
| 56 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { | 72 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { |
| 57 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; | 73 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; |
| @@ -776,6 +792,9 @@ in { | |||
| 776 | "surtr.yggdrasil.li" = { | 792 | "surtr.yggdrasil.li" = { |
| 777 | restartUnits = [ "postfix.service" "dovecot.service" ]; | 793 | restartUnits = [ "postfix.service" "dovecot.service" ]; |
| 778 | }; | 794 | }; |
| 795 | "pw.bouncy.email" = { | ||
| 796 | restartUnits = [ "nginx.service" ]; | ||
| 797 | }; | ||
| 779 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) | 798 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) |
| 780 | // listToAttrs (concatMap (domain: [ | 799 | // listToAttrs (concatMap (domain: [ |
| 781 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) | 800 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) |
| @@ -814,6 +833,11 @@ in { | |||
| 814 | "unix:/run/spm/server.sock" = {}; | 833 | "unix:/run/spm/server.sock" = {}; |
| 815 | }; | 834 | }; |
| 816 | }; | 835 | }; |
| 836 | upstreams.password-server = { | ||
| 837 | servers = { | ||
| 838 | "unix:/run/email-password-server.sock" = {}; | ||
| 839 | }; | ||
| 840 | }; | ||
| 817 | 841 | ||
| 818 | virtualHosts = listToAttrs (map (domain: nameValuePair "spm.${domain}" { | 842 | virtualHosts = listToAttrs (map (domain: nameValuePair "spm.${domain}" { |
| 819 | forceSSL = true; | 843 | forceSSL = true; |
| @@ -868,7 +892,28 @@ in { | |||
| 868 | ''; | 892 | ''; |
| 869 | }; | 893 | }; |
| 870 | }; | 894 | }; |
| 871 | }) emailDomains); | 895 | }) emailDomains) // { |
| 896 | "pw.bouncy.email" = { | ||
| 897 | forceSSL = true; | ||
| 898 | kTLS = true; | ||
| 899 | http3 = false; | ||
| 900 | sslCertificate = "/run/credentials/nginx.service/pw.bouncy.email.pem"; | ||
| 901 | sslCertificateKey = "/run/credentials/nginx.service/pw.bouncy.email.key.pem"; | ||
| 902 | extraConfig = '' | ||
| 903 | ssl_stapling off; | ||
| 904 | ssl_verify_client optional; | ||
| 905 | ssl_client_certificate ${toString ./ca/ca.crt}; | ||
| 906 | ''; | ||
| 907 | locations."/" = { | ||
| 908 | proxyPass = "http://password-server"; | ||
| 909 | |||
| 910 | extraConfig = '' | ||
| 911 | proxy_set_header SSL-CLIENT-VERIFY $ssl_client_verify; | ||
| 912 | proxy_set_header SSL-CLIENT-S-DN $ssl_client_s_dn; | ||
| 913 | '';} | ||
| 914 | ; | ||
| 915 | }; | ||
| 916 | }; | ||
| 872 | }; | 917 | }; |
| 873 | 918 | ||
| 874 | systemd.services.nginx.serviceConfig.LoadCredential = concatMap (domain: [ | 919 | systemd.services.nginx.serviceConfig.LoadCredential = concatMap (domain: [ |
| @@ -878,7 +923,10 @@ in { | |||
| 878 | "mta-sts.${domain}.key.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/key.pem" | 923 | "mta-sts.${domain}.key.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/key.pem" |
| 879 | "mta-sts.${domain}.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/fullchain.pem" | 924 | "mta-sts.${domain}.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/fullchain.pem" |
| 880 | "mta-sts.${domain}.chain.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/chain.pem" | 925 | "mta-sts.${domain}.chain.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/chain.pem" |
| 881 | ]) emailDomains; | 926 | ]) emailDomains ++ [ |
| 927 | "pw.bouncy.email.key.pem:${config.security.acme.certs."pw.bouncy.email".directory}/key.pem" | ||
| 928 | "pw.bouncy.email.pem:${config.security.acme.certs."pw.bouncy.email".directory}/fullchain.pem" | ||
| 929 | ]; | ||
| 882 | 930 | ||
| 883 | systemd.services.spm = { | 931 | systemd.services.spm = { |
| 884 | serviceConfig = { | 932 | serviceConfig = { |
| @@ -1021,6 +1069,62 @@ in { | |||
| 1021 | }; | 1069 | }; |
| 1022 | users.groups."postfix-internal-policy" = {}; | 1070 | users.groups."postfix-internal-policy" = {}; |
| 1023 | 1071 | ||
| 1072 | systemd.sockets."email-password-server" = { | ||
| 1073 | requiredBy = ["postfix.service"]; | ||
| 1074 | wants = ["email-password-server.service"]; | ||
| 1075 | socketConfig = { | ||
| 1076 | ListenStream = "/run/email-password-server.sock"; | ||
| 1077 | }; | ||
| 1078 | }; | ||
| 1079 | systemd.services."email-password-server" = { | ||
| 1080 | after = [ "postgresql.service" ]; | ||
| 1081 | bindsTo = [ "postgresql.service" ]; | ||
| 1082 | |||
| 1083 | serviceConfig = { | ||
| 1084 | Type = "notify"; | ||
| 1085 | |||
| 1086 | ExecStart = getExe' password-server "password-server"; | ||
| 1087 | |||
| 1088 | Environment = [ | ||
| 1089 | "PGDATABASE=email" | ||
| 1090 | ]; | ||
| 1091 | |||
| 1092 | LoadCredential = [ | ||
| 1093 | "secret:${config.sops.secrets."email-password-server-secret".path}" | ||
| 1094 | ]; | ||
| 1095 | |||
| 1096 | DynamicUser = false; | ||
| 1097 | User = "email-password-server"; | ||
| 1098 | Group = "email-password-server"; | ||
| 1099 | ProtectSystem = "strict"; | ||
| 1100 | SystemCallFilter = "@system-service"; | ||
| 1101 | NoNewPrivileges = true; | ||
| 1102 | ProtectKernelTunables = true; | ||
| 1103 | ProtectKernelModules = true; | ||
| 1104 | ProtectKernelLogs = true; | ||
| 1105 | ProtectControlGroups = true; | ||
| 1106 | MemoryDenyWriteExecute = true; | ||
| 1107 | RestrictSUIDSGID = true; | ||
| 1108 | KeyringMode = "private"; | ||
| 1109 | ProtectClock = true; | ||
| 1110 | RestrictRealtime = true; | ||
| 1111 | PrivateDevices = true; | ||
| 1112 | PrivateTmp = true; | ||
| 1113 | ProtectHostname = true; | ||
| 1114 | ReadWritePaths = ["/run/postgresql"]; | ||
| 1115 | }; | ||
| 1116 | }; | ||
| 1117 | users.users."email-password-server" = { | ||
| 1118 | isSystemUser = true; | ||
| 1119 | group = "email-password-server"; | ||
| 1120 | }; | ||
| 1121 | users.groups."email-password-server" = {}; | ||
| 1122 | sops.secrets."email-password-server-secret" = { | ||
| 1123 | format = "binary"; | ||
| 1124 | sopsFile = ./email-password-server-secret; | ||
| 1125 | }; | ||
| 1126 | |||
| 1127 | |||
| 1024 | services.postfwd = { | 1128 | services.postfwd = { |
| 1025 | enable = true; | 1129 | enable = true; |
| 1026 | cache = false; | 1130 | cache = false; |
