{ lib, pkgs, ... }: { services.postgresql = { ensureDatabases = [ "zitadel" ]; ensureUsers = [{ name = "zitadel"; ensureDBOwnership = true; }]; }; services.zitadel = { enable = true; settings = { # We seem to be unable to bind Zitadel to only the loopback interface. Port = 9068; ExternalPort = 443; ExternalDomain = "zd.mou.fo"; Database.postgres = { Host = "127.0.0.1"; Port = 5432; Database = "zitadel"; User.Username = "zitadel"; User.SSL.Mode = "disable"; }; }; masterKeyFile = "/run/credentials/zitadel.service/master.key"; # Zitadel only connects to Postgres over the network, so we need to # configure passwords here and manually for the zitadel Postgres user. extraSettingsPaths = [ "/run/credentials/zitadel.service/secrets.yaml" ]; }; # TODO should be delayed after postgres systemd.services.zitadel = { # Shim into PATH to avoid start-from-init which requires Postgres admin # credentials. See https://github.com/zitadel/zitadel/issues/4304 path = let wrapper = pkgs.writeShellScriptBin "zitadel" '' shift exec ${pkgs.zitadel}/bin/zitadel start-from-setup "$@" ''; in lib.mkBefore [ wrapper ]; # Allow unprivileged zitadel user selective access to secrets. serviceConfig.LoadCredential = [ "master.key:/var/secrets/zitadel.key" "secrets.yaml:/var/secrets/zitadel.yaml" ]; }; services.nginx.virtualHosts."zd.mou.fo" = { enableACME = true; forceSSL = true; locations."/".proxyPass = "http://127.0.0.1:9068"; }; # The oauth2-proxy module has a magic nginx.virtualHosts option that rewrites # nginx configs. It can be heavy handed, but we use it for brevity. In # particular, it configures Traefik-like ForwardAuth authentication with # auth_request. Note if this resource is missing for whatever reason, the # module magic will fail open (auth_request unset). services.oauth2-proxy = { enable = true; cookie.domain = "mou.fo"; nginx.domain = "op.mou.fo"; setXauthrequest = true; # let oauth2-proxy nginx module pass X-User/X-Email reverseProxy = true; provider = "oidc"; clientID = "288565372746006652@mou.fo"; oidcIssuerUrl = "https://zd.mou.fo"; # Sets OAUTH2_PROXY_COOKIE_SECRET and OAUTH2_PROXY_CLIENT_SECRET. keyFile = "/var/secrets/oauth2-proxy.env"; # Ignore e-mail address. email.domains = [ "*" ]; extraConfig = { code-challenge-method = "S256"; whitelist-domain = ".mou.fo"; # allowed redirects after authentication # https://github.com/oauth2-proxy/oauth2-proxy/issues/1612#issuecomment-1099217761 oidc-email-claim = "sub"; }; }; # Kludge to bring up after Kanidm (otherwise OIDC discovery fails). A simple # ordering dependency isn't enough because kandim.service is active before # Kanidm responds to requests. Kanidm starts up quickly enough that boot up # may work without this. systemd.services.oauth2-proxy.serviceConfig.RestartSec = 5; # It's somewhat overkill to assign oauth2-proxy its own domain. We can't use # Kanidm's though, because it uses the /oauth2 path which the oauth2-proxy # nginx module is hardcoded for (proxyPrefix is ignored); this module magic is # also why we do not need to explicitly specify proxyPass. services.nginx.virtualHosts."op.mou.fo" = { enableACME = true; forceSSL = true; }; }