From 5b4f1110443d01a3a0f4b73e01c1b44be7560276 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 6 Jun 2026 18:45:28 +0200 Subject: pw.bouncy.email --- hosts/surtr/email/default.nix | 108 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) (limited to 'hosts/surtr/email/default.nix') 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 mainProgram = "internal-policy-server"; }; }); + password-server = + let + workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./password-server; }; + pythonSet = flake.lib.pythonSet { + inherit pkgs; + python = pkgs.python3; + overlay = workspace.mkPyprojectOverlay { + sourcePreference = "wheel"; + }; + }; + virtualEnv = pythonSet.mkVirtualEnv "password-server-env" workspace.deps.default; + in virtualEnv.overrideAttrs (oldAttrs: { + meta = (oldAttrs.meta or {}) // { + mainProgram = "password-server"; + }; + }); nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { inputs = with pkgs; [inetutils nftables gnugrep findutils]; @@ -776,6 +792,9 @@ in { "surtr.yggdrasil.li" = { restartUnits = [ "postfix.service" "dovecot.service" ]; }; + "pw.bouncy.email" = { + restartUnits = [ "nginx.service" ]; + }; } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) // listToAttrs (concatMap (domain: [ (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) @@ -814,6 +833,11 @@ in { "unix:/run/spm/server.sock" = {}; }; }; + upstreams.password-server = { + servers = { + "unix:/run/email-password-server.sock" = {}; + }; + }; virtualHosts = listToAttrs (map (domain: nameValuePair "spm.${domain}" { forceSSL = true; @@ -868,7 +892,28 @@ in { ''; }; }; - }) emailDomains); + }) emailDomains) // { + "pw.bouncy.email" = { + forceSSL = true; + kTLS = true; + http3 = false; + sslCertificate = "/run/credentials/nginx.service/pw.bouncy.email.pem"; + sslCertificateKey = "/run/credentials/nginx.service/pw.bouncy.email.key.pem"; + extraConfig = '' + ssl_stapling off; + ssl_verify_client optional; + ssl_client_certificate ${toString ./ca/ca.crt}; + ''; + locations."/" = { + proxyPass = "http://password-server"; + + extraConfig = '' + proxy_set_header SSL-CLIENT-VERIFY $ssl_client_verify; + proxy_set_header SSL-CLIENT-S-DN $ssl_client_s_dn; + '';} + ; + }; + }; }; systemd.services.nginx.serviceConfig.LoadCredential = concatMap (domain: [ @@ -878,7 +923,10 @@ in { "mta-sts.${domain}.key.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/key.pem" "mta-sts.${domain}.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/fullchain.pem" "mta-sts.${domain}.chain.pem:${config.security.acme.certs."mta-sts.${domain}".directory}/chain.pem" - ]) emailDomains; + ]) emailDomains ++ [ + "pw.bouncy.email.key.pem:${config.security.acme.certs."pw.bouncy.email".directory}/key.pem" + "pw.bouncy.email.pem:${config.security.acme.certs."pw.bouncy.email".directory}/fullchain.pem" + ]; systemd.services.spm = { serviceConfig = { @@ -1021,6 +1069,62 @@ in { }; users.groups."postfix-internal-policy" = {}; + systemd.sockets."email-password-server" = { + requiredBy = ["postfix.service"]; + wants = ["email-password-server.service"]; + socketConfig = { + ListenStream = "/run/email-password-server.sock"; + }; + }; + systemd.services."email-password-server" = { + after = [ "postgresql.service" ]; + bindsTo = [ "postgresql.service" ]; + + serviceConfig = { + Type = "notify"; + + ExecStart = getExe' password-server "password-server"; + + Environment = [ + "PGDATABASE=email" + ]; + + LoadCredential = [ + "secret:${config.sops.secrets."email-password-server-secret".path}" + ]; + + DynamicUser = false; + User = "email-password-server"; + Group = "email-password-server"; + ProtectSystem = "strict"; + SystemCallFilter = "@system-service"; + NoNewPrivileges = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectKernelLogs = true; + ProtectControlGroups = true; + MemoryDenyWriteExecute = true; + RestrictSUIDSGID = true; + KeyringMode = "private"; + ProtectClock = true; + RestrictRealtime = true; + PrivateDevices = true; + PrivateTmp = true; + ProtectHostname = true; + ReadWritePaths = ["/run/postgresql"]; + }; + }; + users.users."email-password-server" = { + isSystemUser = true; + group = "email-password-server"; + }; + users.groups."email-password-server" = {}; + sops.secrets."email-password-server-secret" = { + format = "binary"; + sopsFile = ./email-password-server-secret; + }; + + services.postfwd = { enable = true; cache = false; -- cgit v1.2.3