diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 5d6ad24461d4..d35b554cbff8 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -62,6 +62,9 @@ "module-boot-plymouth-tpm2-totp-quick-start-enable": [ "index.html#module-boot-plymouth-tpm2-totp-quick-start-enable" ], + "module-services-keycloak-unix-socket": [ + "index.html#module-services-keycloak-unix-socket" + ], "module-services-tandoor-recipes-migrating-media-option-1": [ "index.html#module-services-tandoor-recipes-migrating-media-option-1" ], diff --git a/nixos/modules/services/web-apps/keycloak.md b/nixos/modules/services/web-apps/keycloak.md index 4bf8c9fcad71..b22b5a8cd282 100644 --- a/nixos/modules/services/web-apps/keycloak.md +++ b/nixos/modules/services/web-apps/keycloak.md @@ -54,6 +54,30 @@ The path should be provided as a string, not a Nix path, since Nix paths are copied into the world readable Nix store. ::: +## Unix socket authentication {#module-services-keycloak-unix-socket} + +For PostgreSQL, Keycloak can connect via Unix socket using peer +authentication, avoiding the need for a database password. + +To use Unix sockets, set [](#opt-services.keycloak.database.host) +to the PostgreSQL socket directory (e.g., `/run/postgresql`) and +add the required junixsocket plugins: +```nix +{ + services.keycloak = { + database.host = "/run/postgresql"; + plugins = with pkgs.keycloak.plugins; [ + junixsocket-common + junixsocket-native-common + ]; + }; +} +``` + +::: {.note} +Unix socket authentication is only supported for PostgreSQL. +::: + ## Hostname {#module-services-keycloak-hostname} The hostname is used to build the public URL used as base for diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix index 8501c2e874ef..66a501c5c267 100644 --- a/nixos/modules/services/web-apps/keycloak.nix +++ b/nixos/modules/services/web-apps/keycloak.nix @@ -167,6 +167,11 @@ in default = "localhost"; description = '' Hostname of the database to connect to. + + For PostgreSQL, this can also be a path to a Unix socket + directory (e.g., `/run/postgresql`) to use peer authentication. + This requires adding `junixsocket-common` and `junixsocket-native-common` + to [](#opt-services.keycloak.plugins). ''; }; @@ -189,11 +194,12 @@ in useSSL = mkOption { type = bool; - default = cfg.database.host != "localhost"; - defaultText = literalExpression ''config.${opt.database.host} != "localhost"''; + default = cfg.database.host != "localhost" && !hasPrefix "/" cfg.database.host; + defaultText = literalExpression ''config.${opt.database.host} != "localhost" && !lib.hasPrefix "/" config.${opt.database.host}''; description = '' - Whether the database connection should be secured by SSL / - TLS. + Whether the database connection should be secured by SSL / TLS. + + Defaults to `false` for localhost and Unix socket connections. ''; }; @@ -252,11 +258,15 @@ in }; passwordFile = mkOption { - type = path; + type = nullOr path; + default = null; example = "/run/keys/db_password"; apply = assertStringPath "passwordFile"; description = '' The path to a file containing the database password. + + Not required when using Unix socket authentication (peer auth) + by setting `host` to a socket path like `/run/postgresql`. ''; }; }; @@ -553,6 +563,13 @@ in for more information. ''; } + { + assertion = cfg.database.passwordFile != null || hasPrefix "/" cfg.database.host; + message = '' + services.keycloak.database.passwordFile must be set unless using + Unix socket authentication (host starting with /). + ''; + } ]; environment.systemPackages = [ keycloakBuild ]; @@ -582,19 +599,32 @@ in "trustCertificateKeyStorePassword=notsosecretpassword" ] ); + + dbName = if databaseActuallyCreateLocally then "keycloak" else cfg.database.name; dbProps = if cfg.database.type == "postgresql" then postgresParams else mariadbParams; + + # Unix socket connection requires junixsocket library and special JDBC URL + isUnixSocket = hasPrefix "/" cfg.database.host; + unixSocketUrl = "jdbc:postgresql://localhost/${dbName}?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=${cfg.database.host}/.s.PGSQL.${toString cfg.database.port}&sslMode=disable"; in mkMerge [ { db = if cfg.database.type == "postgresql" then "postgres" else cfg.database.type; db-username = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username; - db-password._secret = cfg.database.passwordFile; + db-password = mkIf (cfg.database.passwordFile != null) { + _secret = cfg.database.passwordFile; + }; + } + (mkIf isUnixSocket { + db-url = unixSocketUrl; + }) + (mkIf (!isUnixSocket) { db-url-host = cfg.database.host; db-url-port = toString cfg.database.port; - db-url-database = if databaseActuallyCreateLocally then "keycloak" else cfg.database.name; + db-url-database = dbName; db-url-properties = prefixUnlessEmpty "?" dbProps; db-url = null; - } + }) (mkIf (cfg.sslCertificate != null && cfg.sslCertificateKey != null) { https-certificate-file = "/run/keycloak/ssl/ssl_cert"; https-certificate-key-file = "/run/keycloak/ssl/ssl_key"; @@ -778,5 +808,8 @@ in }; meta.doc = ./keycloak.md; - meta.maintainers = [ maintainers.talyz ]; + meta.maintainers = with maintainers; [ + talyz + anish + ]; } diff --git a/pkgs/by-name/ke/keycloak/all-plugins.nix b/pkgs/by-name/ke/keycloak/all-plugins.nix index 29c1e54c0108..f000a43a06e7 100644 --- a/pkgs/by-name/ke/keycloak/all-plugins.nix +++ b/pkgs/by-name/ke/keycloak/all-plugins.nix @@ -1,4 +1,9 @@ -{ callPackage, fetchMavenArtifact }: +{ + callPackage, + fetchMavenArtifact, + junixsocket-common, + junixsocket-native-common, +}: { scim-for-keycloak = callPackage ./scim-for-keycloak { }; @@ -9,6 +14,11 @@ keycloak-restrict-client-auth = callPackage ./keycloak-restrict-client-auth { }; keycloak-remember-me-authenticator = callPackage ./keycloak-remember-me-authenticator { }; + # junixsocket provides Unix domain socket support for JDBC connections, + # which is required for connecting to PostgreSQL via Unix socket. + junixsocket-common = junixsocket-common.passthru.jar; + junixsocket-native-common = junixsocket-native-common.passthru.jar; + # These could theoretically be used by something other than Keycloak, but # there are no other quarkus apps in nixpkgs (as of 2023-08-21) quarkus-systemd-notify =