···
frontendUrl = lib.mkOption {
57
+
apply = x: if lib.hasSuffix "/" x then x else x + "/";
example = "keycloak.example.com/auth";
The public URL used as base for all frontend requests. Should
···
87
-
certificatePrivateKeyBundle = lib.mkOption {
88
+
sslCertificate = lib.mkOption {
type = lib.types.nullOr lib.types.path;
example = "/run/keys/ssl_cert";
92
-
The path to a PEM formatted bundle of the private key and
93
-
certificate to use for TLS connections.
93
+
The path to a PEM formatted certificate to use for TLS/SSL
This should be a string, not a Nix path, since Nix paths are
copied into the world-readable Nix store.
100
-
databaseType = lib.mkOption {
101
-
type = lib.types.enum [ "mysql" "postgresql" ];
102
-
default = "postgresql";
101
+
sslCertificateKey = lib.mkOption {
102
+
type = lib.types.nullOr lib.types.path;
104
+
example = "/run/keys/ssl_key";
105
-
The type of database Keycloak should connect to.
106
+
The path to a PEM formatted private key to use for TLS/SSL
109
-
databaseHost = lib.mkOption {
110
-
type = lib.types.str;
111
-
default = "localhost";
113
-
Hostname of the database to connect to.
109
+
This should be a string, not a Nix path, since Nix paths are
110
+
copied into the world-readable Nix store.
125
-
type = lib.types.port;
126
-
default = dbPorts.${cfg.databaseType};
128
-
Port of the database to connect to.
115
+
type = lib.mkOption {
116
+
type = lib.types.enum [ "mysql" "postgresql" ];
117
+
default = "postgresql";
120
+
The type of database Keycloak should connect to.
132
-
databaseUseSSL = lib.mkOption {
133
-
type = lib.types.bool;
134
-
default = cfg.databaseHost != "localhost";
136
-
Whether the database connection should be secured by SSL /
124
+
host = lib.mkOption {
125
+
type = lib.types.str;
126
+
default = "localhost";
128
+
Hostname of the database to connect to.
141
-
databaseCaCert = lib.mkOption {
142
-
type = lib.types.nullOr lib.types.path;
145
-
The SSL / TLS CA certificate that verifies the identity of the
140
+
type = lib.types.port;
141
+
default = dbPorts.${cfg.database.type};
143
+
Port of the database to connect to.
148
-
Required when PostgreSQL is used and SSL is turned on.
147
+
useSSL = lib.mkOption {
148
+
type = lib.types.bool;
149
+
default = cfg.database.host != "localhost";
151
+
Whether the database connection should be secured by SSL /
150
-
For MySQL, if left at <literal>null</literal>, the default
151
-
Java keystore is used, which should suffice if the server
152
-
certificate is issued by an official CA.
156
+
caCert = lib.mkOption {
157
+
type = lib.types.nullOr lib.types.path;
160
+
The SSL / TLS CA certificate that verifies the identity of the
156
-
databaseCreateLocally = lib.mkOption {
157
-
type = lib.types.bool;
160
-
Whether a database should be automatically created on the
161
-
local host. Set this to false if you plan on provisioning a
162
-
local database yourself. This has no effect if
163
-
services.keycloak.databaseHost is customized.
163
+
Required when PostgreSQL is used and SSL is turned on.
167
-
databaseUsername = lib.mkOption {
168
-
type = lib.types.str;
169
-
default = "keycloak";
171
-
Username to use when connecting to an external or manually
172
-
provisioned database; has no effect when a local database is
173
-
automatically provisioned.
165
+
For MySQL, if left at <literal>null</literal>, the default
166
+
Java keystore is used, which should suffice if the server
167
+
certificate is issued by an official CA.
175
-
To use this with a local database, set <xref
176
-
linkend="opt-services.keycloak.databaseCreateLocally" /> to
177
-
<literal>false</literal> and create the database and user
178
-
manually. The database should be called
179
-
<literal>keycloak</literal>.
171
+
createLocally = lib.mkOption {
172
+
type = lib.types.bool;
175
+
Whether a database should be automatically created on the
176
+
local host. Set this to false if you plan on provisioning a
177
+
local database yourself. This has no effect if
178
+
services.keycloak.database.host is customized.
183
-
databasePasswordFile = lib.mkOption {
184
-
type = lib.types.path;
185
-
example = "/run/keys/db_password";
187
-
File containing the database password.
182
+
username = lib.mkOption {
183
+
type = lib.types.str;
184
+
default = "keycloak";
186
+
Username to use when connecting to an external or manually
187
+
provisioned database; has no effect when a local database is
188
+
automatically provisioned.
189
-
This should be a string, not a Nix path, since Nix paths are
190
-
copied into the world-readable Nix store.
190
+
To use this with a local database, set <xref
191
+
linkend="opt-services.keycloak.database.createLocally" /> to
192
+
<literal>false</literal> and create the database and user
193
+
manually. The database should be called
194
+
<literal>keycloak</literal>.
198
+
passwordFile = lib.mkOption {
199
+
type = lib.types.path;
200
+
example = "/run/keys/db_password";
202
+
File containing the database password.
204
+
This should be a string, not a Nix path, since Nix paths are
205
+
copied into the world-readable Nix store.
···
# We only want to create a database if we're actually going to connect to it.
264
-
databaseActuallyCreateLocally = cfg.databaseCreateLocally && cfg.databaseHost == "localhost";
265
-
createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.databaseType == "postgresql";
266
-
createLocalMySQL = databaseActuallyCreateLocally && cfg.databaseType == "mysql";
280
+
databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "localhost";
281
+
createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql";
282
+
createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql";
mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} ''
269
-
${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.databaseCaCert} -keystore $out -storepass notsosecretpassword -noprompt
285
+
${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
keycloakConfig' = builtins.foldl' lib.recursiveUpdate {
···
"subsystem=datasources"."data-source=KeycloakDS" = {
285
-
user-name = if databaseActuallyCreateLocally then "keycloak" else cfg.databaseUsername;
301
+
user-name = if databaseActuallyCreateLocally then "keycloak" else cfg.database.username;
password = "@db-password@";
289
-
(lib.optionalAttrs (cfg.databaseType == "postgresql") {
305
+
(lib.optionalAttrs (cfg.database.type == "postgresql") {
"subsystem=datasources" = {
"jdbc-driver=postgresql" = {
driver-module-name = "org.postgresql";
···
driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource";
"data-source=KeycloakDS" = {
297
-
connection-url = "jdbc:postgresql://${cfg.databaseHost}:${builtins.toString cfg.databasePort}/keycloak";
313
+
connection-url = "jdbc:postgresql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
driver-name = "postgresql";
299
-
"connection-properties=ssl".value = lib.boolToString cfg.databaseUseSSL;
300
-
} // (lib.optionalAttrs (cfg.databaseCaCert != null) {
301
-
"connection-properties=sslrootcert".value = cfg.databaseCaCert;
315
+
"connection-properties=ssl".value = lib.boolToString cfg.database.useSSL;
316
+
} // (lib.optionalAttrs (cfg.database.caCert != null) {
317
+
"connection-properties=sslrootcert".value = cfg.database.caCert;
"connection-properties=sslmode".value = "verify-ca";
306
-
(lib.optionalAttrs (cfg.databaseType == "mysql") {
322
+
(lib.optionalAttrs (cfg.database.type == "mysql") {
"subsystem=datasources" = {
driver-module-name = "com.mysql";
···
driver-class-name = "com.mysql.jdbc.Driver";
"data-source=KeycloakDS" = {
314
-
connection-url = "jdbc:mysql://${cfg.databaseHost}:${builtins.toString cfg.databasePort}/keycloak";
330
+
connection-url = "jdbc:mysql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
316
-
"connection-properties=useSSL".value = lib.boolToString cfg.databaseUseSSL;
317
-
"connection-properties=requireSSL".value = lib.boolToString cfg.databaseUseSSL;
318
-
"connection-properties=verifyServerCertificate".value = lib.boolToString cfg.databaseUseSSL;
332
+
"connection-properties=useSSL".value = lib.boolToString cfg.database.useSSL;
333
+
"connection-properties=requireSSL".value = lib.boolToString cfg.database.useSSL;
334
+
"connection-properties=verifyServerCertificate".value = lib.boolToString cfg.database.useSSL;
"connection-properties=characterEncoding".value = "UTF-8";
valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker";
validate-on-match = true;
exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter";
323
-
} // (lib.optionalAttrs (cfg.databaseCaCert != null) {
339
+
} // (lib.optionalAttrs (cfg.database.caCert != null) {
"connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}";
"connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword";
329
-
(lib.optionalAttrs (cfg.certificatePrivateKeyBundle != null) {
345
+
(lib.optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) {
"socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort;
"core-service=management"."security-realm=UndertowRealm"."server-identity=ssl" = {
keystore-path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
···
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
540
-
keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {} ''
556
+
keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {
557
+
nativeBuildInputs = [ cfg.package ];
export JBOSS_BASE_DIR="$(pwd -P)";
export JBOSS_MODULEPATH="${cfg.package}/modules";
export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log";
···
mkdir -p {deployments,ssl}
550
-
"${cfg.package}/bin/standalone.sh"&
554
-
while ! ${cfg.package}/bin/jboss-cli.sh --connect ':read-attribute(name=server-state)'; do
572
+
while ! jboss-cli.sh --connect ':read-attribute(name=server-state)'; do
if [[ "$attempt" == "$max_attempts" ]]; then
echo "ERROR: Could not connect to Keycloak after $attempt attempts! Failing.." >&2
···
564
-
${cfg.package}/bin/jboss-cli.sh --connect --file=${jbossCliScript} --echo-command
582
+
jboss-cli.sh --connect --file=${jbossCliScript} --echo-command
cp configuration/standalone.xml $out
···
573
-
assertion = (cfg.databaseUseSSL && cfg.databaseType == "postgresql") -> (cfg.databaseCaCert != null);
574
-
message = "A CA certificate must be specified (in 'services.keycloak.databaseCaCert') when PostgreSQL is used with SSL";
591
+
assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
592
+
message = "A CA certificate must be specified (in 'services.keycloak.database.caCert') when PostgreSQL is used with SSL";
···
after = [ "postgresql.service" ];
before = [ "keycloak.service" ];
bindsTo = [ "postgresql.service" ];
602
+
path = [ config.services.postgresql.package ];
···
610
+
set -o errexit -o pipefail -o nounset -o errtrace
611
+
shopt -s inherit_errexit
593
-
PSQL=${config.services.postgresql.package}/bin/psql
613
+
create_role="$(mktemp)"
614
+
trap 'rm -f "$create_role"' ERR EXIT
595
-
db_password="$(<'${cfg.databasePasswordFile}')"
596
-
$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || $PSQL -tAc "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB"
597
-
$PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
616
+
echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$(<'${cfg.database.passwordFile}')' CREATEDB" > "$create_role"
617
+
psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || psql -tA --file="$create_role"
618
+
psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || psql -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
···
after = [ "mysql.service" ];
before = [ "keycloak.service" ];
bindsTo = [ "mysql.service" ];
626
+
path = [ config.services.mysql.package ];
···
Group = config.services.mysql.group;
634
+
set -o errexit -o pipefail -o nounset -o errtrace
635
+
shopt -s inherit_errexit
614
-
db_password="$(<'${cfg.databasePasswordFile}')"
637
+
db_password="$(<'${cfg.database.passwordFile}')"
( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
echo "CREATE DATABASE keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';"
618
-
) | ${config.services.mysql.package}/bin/mysql -N
···
bindsTo = databaseServices;
wantedBy = [ "multi-user.target" ];
···
startPreFullPrivileges = ''
672
+
set -o errexit -o pipefail -o nounset -o errtrace
673
+
shopt -s inherit_errexit
649
-
install -T -m 0400 -o keycloak -g keycloak '${cfg.databasePasswordFile}' /run/keycloak/secrets/db_password
650
-
'' + lib.optionalString (cfg.certificatePrivateKeyBundle != null) ''
651
-
install -T -m 0400 -o keycloak -g keycloak '${cfg.certificatePrivateKeyBundle}' /run/keycloak/secrets/ssl_cert_pk_bundle
677
+
install -T -m 0400 -o keycloak -g keycloak '${cfg.database.passwordFile}' /run/keycloak/secrets/db_password
678
+
'' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
679
+
install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificate}' /run/keycloak/secrets/ssl_cert
680
+
install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificateKey}' /run/keycloak/secrets/ssl_key
683
+
set -o errexit -o pipefail -o nounset -o errtrace
684
+
shopt -s inherit_errexit
install -m 0600 ${cfg.package}/standalone/configuration/*.properties /run/keycloak/configuration
install -T -m 0600 ${keycloakConfig} /run/keycloak/configuration/standalone.xml
···
replace-secret '@db-password@' '/run/keycloak/secrets/db_password' /run/keycloak/configuration/standalone.xml
export JAVA_OPTS=-Djboss.server.config.user.dir=/run/keycloak/configuration
662
-
${cfg.package}/bin/add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
663
-
'' + lib.optionalString (cfg.certificatePrivateKeyBundle != null) ''
694
+
add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
695
+
'' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
665
-
cat /run/keycloak/secrets/ssl_cert_pk_bundle <(echo) /etc/ssl/certs/ca-certificates.crt > allcerts.pem
666
-
${pkgs.openssl}/bin/openssl pkcs12 -export -in /run/keycloak/secrets/ssl_cert_pk_bundle -chain \
667
-
-name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
668
-
-CAfile allcerts.pem -passout pass:notsosecretpassword
697
+
cat /run/keycloak/secrets/ssl_cert <(echo) \
698
+
/run/keycloak/secrets/ssl_key <(echo) \
699
+
/etc/ssl/certs/ca-certificates.crt \
701
+
openssl pkcs12 -export -in /run/keycloak/secrets/ssl_cert -inkey /run/keycloak/secrets/ssl_key -chain \
702
+
-name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
703
+
-CAfile allcerts.pem -passout pass:notsosecretpassword
···
meta.doc = ./keycloak.xml;
735
+
meta.maintainers = [ lib.maintainers.talyz ];