diff options
| author | Joe Mou <dev@mou.fo> | 2024-07-02 18:07:28 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2024-10-09 13:48:41 -0400 |
| commit | 26479ad1b9e441bd45642cf70a47e92f11cd6b32 (patch) | |
| tree | 4caaa7e020334b9a5f4ee900c60fb22a094c73aa /hostnix/elmo/oidc.nix | |
| parent | 19805b91322878264cc235c880c04fdabafa0949 (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/elmo/oidc.nix')
| -rw-r--r-- | hostnix/elmo/oidc.nix | 86 |
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; + }; } |
