summaryrefslogtreecommitdiff
path: root/hostnix
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2024-07-02 18:07:28 -0400
committerJoe Mou <dev@mou.fo>2024-10-09 13:48:41 -0400
commit26479ad1b9e441bd45642cf70a47e92f11cd6b32 (patch)
tree4caaa7e020334b9a5f4ee900c60fb22a094c73aa /hostnix
parent19805b91322878264cc235c880c04fdabafa0949 (diff)
Replace Keycloak with Kanidm
Keycloak has always been heavyweight and cumbersome. Kanidm is meant to be an all-in-one Rust identity provider instead. $ sudo kanidmd recover-account idm_admin $ kanidm login --name idm_admin $ kanidm group account-policy credential-type-minimum idm_all_persons any $ kanidm person create joe Joe $ kanidm person credential update joe $ kanidm system oauth2 create oauth2-proxy 'OAuth2 Proxy' https://op.mou.fo $ kanidm system oauth2 update-scope-map oauth2-proxy idm_all_persons openid profile email $ kanidm system oauth2 show-basic-secret oauth2-proxy Passkeys don't work with KeePassXC on Firefox. They might work with Chrome or BitWarden. We disable TOTP for password authentication. Kanidm itself has considered and rejected forward auth support per https://github.com/kanidm/kanidm/issues/2774 With this arrangement session cookies are about 2k. While large these should fit within the default nginx buffers. Dex can be used as a simple identity provider, although it is more designed to facilitate app authentication. It can be configured to have a workable configuration with no persistent state and only staticClients and staticPasswords for resource servers and users. Vouch Proxy is comparable with oauth2-proxy. Both assume the user has an e-mail which we don't use. However oauth2-proxy seems to have better workarounds and is somewhat more actively maintained. Vouch Proxy also lacks a NixOS module. https://discourse.nixos.org/t/configuring-vouch-proxy-or-oauth2-proxy-nginx-nix/19337/2 https://github.com/vouch/vouch-proxy/issues/309
Diffstat (limited to 'hostnix')
-rw-r--r--hostnix/elmo/oidc.nix86
1 files changed, 53 insertions, 33 deletions
diff --git a/hostnix/elmo/oidc.nix b/hostnix/elmo/oidc.nix
index 0ed170e..b24b070 100644
--- a/hostnix/elmo/oidc.nix
+++ b/hostnix/elmo/oidc.nix
@@ -1,60 +1,80 @@
-{ ... }:
+{ config, ... }:
{
- services.keycloak = {
- enable = true;
- database.passwordFile = "/var/secrets/keycloak.dbpass";
- settings = {
- hostname = "kc.mou.fo";
- http-host = "127.0.0.1";
- http-port = 7567;
- proxy = "edge";
+ services.kanidm = {
+ enableClient = true;
+ enableServer = true;
+ clientSettings = {
+ uri = "https://ki.mou.fo";
+ };
+ serverSettings = {
+ origin = "https://ki.mou.fo";
+ domain = "ki.mou.fo";
+ bindaddress = "[::1]:7368";
+ trust_x_forward_for = true;
+ # Kanidm requires TLS even behind a reverse proxy.
+ tls_chain = "/run/credentials/kanidm.service/fullchain.pem";
+ tls_key = "/run/credentials/kanidm.service/key.pem";
};
};
- services.nginx.virtualHosts."kc.mou.fo" = {
+ systemd.services.kanidm = {
+ # Kanidm runs as an unprivileged user that needs access to certificates.
+ serviceConfig.LoadCredential = let
+ certDir = config.security.acme.certs."ki.mou.fo".directory;
+ in
+ [
+ "fullchain.pem:${certDir}/fullchain.pem"
+ "key.pem:${certDir}/key.pem"
+ ];
+ };
+
+ services.nginx.virtualHosts."ki.mou.fo" = {
enableACME = true;
forceSSL = true;
- locations."/".proxyPass = "http://127.0.0.1:7567";
- # We can handle oauth2-proxy callbacks on any subdomain, but the Keycloak
- # subdomain is the least arbitrary.
- locations."/oauth2/".proxyPass = "http://127.0.0.1:4180";
+ locations."/".proxyPass = "https://[::1]:7368";
};
- # Work around "upstream sent too big header" because of large tokens.
- services.nginx.appendHttpConfig = ''
- proxy_buffers 8 16k;
- proxy_buffer_size 16k;
- '';
-
# The oauth2-proxy module has a magic nginx.virtualHosts option that rewrites
- # nginx configs. It's mostly unhelpful, but we use it for brevity. In
+ # 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 = "kc.mou.fo";
- setXauthrequest = true; # include claims
- email.domains = [ "*" ]; # allow any authenticated user
- # https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/keycloak_oidc
- provider = "keycloak-oidc";
+ nginx.domain = "op.mou.fo";
+ setXauthrequest = true; # let oauth2-proxy nginx module pass X-User/X-Email
+ reverseProxy = true;
+ # Example nestled in https://kanidm.github.io/kanidm/stable/examples/kubernetes_ingress.html
+ provider = "oidc";
clientID = "oauth2-proxy";
+ oidcIssuerUrl = "https://ki.mou.fo/oauth2/openid/oauth2-proxy";
# Sets OAUTH2_PROXY_COOKIE_SECRET and OAUTH2_PROXY_CLIENT_SECRET.
keyFile = "/var/secrets/oauth2-proxy.env";
- redirectURL = "https://kc.mou.fo/oauth2/callback";
+ # Ignore e-mail address.
+ scope = "openid profile";
+ email.domains = [ "*" ];
extraConfig = {
- "oidc-issuer-url" = "https://kc.mou.fo/realms/prod";
- "whitelist-domain" = ".mou.fo";
+ "code-challenge-method" = "S256";
+ "whitelist-domain" = ".mou.fo"; # allowed redirects after authentication
# https://github.com/oauth2-proxy/oauth2-proxy/issues/1612#issuecomment-1099217761
- "insecure-oidc-allow-unverified-email" = true;
"oidc-email-claim" = "sub";
};
};
- # Kludge to bring up after KeyCloak (otherwise OIDC discovery fails). A simple
- # ordering dependency isn't enough because keycloak.service is active before
- # KeyCloak responds to requests.
+ # 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;
+ };
}