keycloak: add PostgreSQL Unix socket authentication support

This commit is contained in:
Anish Pallati
2025-12-29 15:09:05 -05:00
parent 56ea2b2f96
commit f68a960158
4 changed files with 80 additions and 10 deletions
+3
View File
@@ -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"
],
@@ -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
+42 -9
View File
@@ -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
];
}
+11 -1
View File
@@ -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 =