Merge pull request #198820 from talyz/keycloak-admin-password

Changed files
+35 -9
nixos
modules
services
web-apps
tests
+25 -2
nixos/modules/services/web-apps/keycloak.nix
···
assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
message = "A CA certificate must be specified (in 'services.keycloak.database.caCert') when PostgreSQL is used with SSL";
}
+
{
+
assertion = createLocalPostgreSQL -> config.services.postgresql.settings.standard_conforming_strings or true;
+
message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably";
+
}
];
environment.systemPackages = [ keycloakBuild ];
···
create_role="$(mktemp)"
trap 'rm -f "$create_role"' EXIT
+
# Read the password from the credentials directory and
+
# escape any single quotes by adding additional single
+
# quotes after them, following the rules laid out here:
+
# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS
db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")"
+
db_password="''${db_password//\'/\'\'}"
+
echo "CREATE ROLE keycloak WITH LOGIN PASSWORD '$db_password' CREATEDB" > "$create_role"
psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='keycloak'" | grep -q 1 || psql -tA --file="$create_role"
psql -tAc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" | grep -q 1 || psql -tAc 'CREATE DATABASE "keycloak" OWNER "keycloak"'
···
script = ''
set -o errexit -o pipefail -o nounset -o errtrace
shopt -s inherit_errexit
+
+
# Read the password from the credentials directory and
+
# escape any single quotes by adding additional single
+
# quotes after them, following the rules laid out here:
+
# https://dev.mysql.com/doc/refman/8.0/en/string-literals.html
db_password="$(<"$CREDENTIALS_DIRECTORY/db_password")"
-
( echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
+
db_password="''${db_password//\'/\'\'}"
+
+
( echo "SET sql_mode = 'NO_BACKSLASH_ESCAPES';"
+
echo "CREATE USER IF NOT EXISTS 'keycloak'@'localhost' IDENTIFIED BY '$db_password';"
echo "CREATE DATABASE IF NOT EXISTS keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
echo "GRANT ALL PRIVILEGES ON keycloak.* TO 'keycloak'@'localhost';"
) | mysql -N
···
${secretReplacements}
+
# Escape any backslashes in the db parameters, since
+
# they're otherwise unexpectedly read as escape
+
# sequences.
+
sed -i '/db-/ s|\\|\\\\|g' /run/keycloak/conf/keycloak.conf
+
'' + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
mkdir -p /run/keycloak/ssl
cp $CREDENTIALS_DIRECTORY/ssl_{cert,key} /run/keycloak/ssl/
'' + ''
export KEYCLOAK_ADMIN=admin
-
export KEYCLOAK_ADMIN_PASSWORD=${cfg.initialAdminPassword}
+
export KEYCLOAK_ADMIN_PASSWORD=${escapeShellArg cfg.initialAdminPassword}
kc.sh start --optimized
'';
};
+10 -7
nixos/tests/keycloak.nix
···
let
certs = import ./common/acme/server/snakeoil-certs.nix;
frontendUrl = "https://${certs.domain}";
-
initialAdminPassword = "h4IhoJFnt2iQIR9";
keycloakTest = import ./make-test-python.nix (
{ pkgs, databaseType, ... }:
+
let
+
initialAdminPassword = "h4Iho\"JFn't2>iQIR9";
+
adminPasswordFile = pkgs.writeText "admin-password" "${initialAdminPassword}";
+
in
{
name = "keycloak";
meta = with pkgs.lib.maintainers; {
···
type = databaseType;
username = "bogus";
name = "also bogus";
-
passwordFile = "${pkgs.writeText "dbPassword" "wzf6vOCbPp6cqTH"}";
+
passwordFile = "${pkgs.writeText "dbPassword" ''wzf6\"vO"Cb\nP>p#6;c&o?eu=q'THE'''H''''E''}";
};
plugins = with config.services.keycloak.package.plugins; [
keycloak-discord
···
keycloak.succeed("""
curl -sSf -d 'client_id=admin-cli' \
-d 'username=admin' \
-
-d 'password=${initialAdminPassword}' \
+
-d "password=$(<${adminPasswordFile})" \
-d 'grant_type=password' \
'${frontendUrl}/realms/master/protocol/openid-connect/token' \
| jq -r '"Authorization: bearer " + .access_token' >admin_auth_header
···
# Register the metrics SPI
keycloak.succeed(
-
"${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt",
-
"KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password '${initialAdminPassword}'",
-
"KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'",
-
"curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"
+
"""${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt""",
+
"""KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password "$(<${adminPasswordFile})" """,
+
"""KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'""",
+
"""curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"""
)
# Publish the realm, including a test OIDC client and user