mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-02-24 05:00:19 +00:00
Merge pull request #155367 from talyz/keycloak-loadcredential
nixos/keycloak: Use LoadCredential to load secrets + module formatting
This commit is contained in:
commit
e5e160e08e
@ -3,12 +3,25 @@
|
|||||||
let
|
let
|
||||||
cfg = config.services.keycloak;
|
cfg = config.services.keycloak;
|
||||||
opt = options.services.keycloak;
|
opt = options.services.keycloak;
|
||||||
|
|
||||||
|
inherit (lib) types mkOption concatStringsSep mapAttrsToList
|
||||||
|
escapeShellArg recursiveUpdate optionalAttrs boolToString mkOrder
|
||||||
|
sort filterAttrs concatMapStringsSep concatStrings mkIf
|
||||||
|
optionalString optionals mkDefault literalExpression hasSuffix
|
||||||
|
foldl' isAttrs filter attrNames elem literalDocBook
|
||||||
|
maintainers;
|
||||||
|
|
||||||
|
inherit (builtins) match typeOf;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.keycloak = {
|
options.services.keycloak =
|
||||||
|
let
|
||||||
enable = lib.mkOption {
|
inherit (types) bool str nullOr attrsOf path enum anything
|
||||||
type = lib.types.bool;
|
package port;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = mkOption {
|
||||||
|
type = bool;
|
||||||
default = false;
|
default = false;
|
||||||
example = true;
|
example = true;
|
||||||
description = ''
|
description = ''
|
||||||
@ -17,8 +30,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
bindAddress = lib.mkOption {
|
bindAddress = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "\${jboss.bind.address:0.0.0.0}";
|
default = "\${jboss.bind.address:0.0.0.0}";
|
||||||
example = "127.0.0.1";
|
example = "127.0.0.1";
|
||||||
description = ''
|
description = ''
|
||||||
@ -29,8 +42,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
httpPort = lib.mkOption {
|
httpPort = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "\${jboss.http.port:80}";
|
default = "\${jboss.http.port:80}";
|
||||||
example = "8080";
|
example = "8080";
|
||||||
description = ''
|
description = ''
|
||||||
@ -41,8 +54,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
httpsPort = lib.mkOption {
|
httpsPort = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "\${jboss.https.port:443}";
|
default = "\${jboss.https.port:443}";
|
||||||
example = "8443";
|
example = "8443";
|
||||||
description = ''
|
description = ''
|
||||||
@ -53,10 +66,10 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
frontendUrl = lib.mkOption {
|
frontendUrl = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
apply = x:
|
apply = x:
|
||||||
if x == "" || lib.hasSuffix "/" x then
|
if x == "" || hasSuffix "/" x then
|
||||||
x
|
x
|
||||||
else
|
else
|
||||||
x + "/";
|
x + "/";
|
||||||
@ -71,8 +84,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
forceBackendUrlToFrontendUrl = lib.mkOption {
|
forceBackendUrlToFrontendUrl = mkOption {
|
||||||
type = lib.types.bool;
|
type = bool;
|
||||||
default = false;
|
default = false;
|
||||||
example = true;
|
example = true;
|
||||||
description = ''
|
description = ''
|
||||||
@ -90,8 +103,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
sslCertificate = lib.mkOption {
|
sslCertificate = mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = nullOr path;
|
||||||
default = null;
|
default = null;
|
||||||
example = "/run/keys/ssl_cert";
|
example = "/run/keys/ssl_cert";
|
||||||
description = ''
|
description = ''
|
||||||
@ -103,8 +116,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
sslCertificateKey = lib.mkOption {
|
sslCertificateKey = mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = nullOr path;
|
||||||
default = null;
|
default = null;
|
||||||
example = "/run/keys/ssl_key";
|
example = "/run/keys/ssl_key";
|
||||||
description = ''
|
description = ''
|
||||||
@ -117,8 +130,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
database = {
|
database = {
|
||||||
type = lib.mkOption {
|
type = mkOption {
|
||||||
type = lib.types.enum [ "mysql" "postgresql" ];
|
type = enum [ "mysql" "postgresql" ];
|
||||||
default = "postgresql";
|
default = "postgresql";
|
||||||
example = "mysql";
|
example = "mysql";
|
||||||
description = ''
|
description = ''
|
||||||
@ -126,8 +139,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
host = lib.mkOption {
|
host = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "localhost";
|
default = "localhost";
|
||||||
description = ''
|
description = ''
|
||||||
Hostname of the database to connect to.
|
Hostname of the database to connect to.
|
||||||
@ -141,27 +154,27 @@ in
|
|||||||
mysql = 3306;
|
mysql = 3306;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
lib.mkOption {
|
mkOption {
|
||||||
type = lib.types.port;
|
type = port;
|
||||||
default = dbPorts.${cfg.database.type};
|
default = dbPorts.${cfg.database.type};
|
||||||
defaultText = lib.literalDocBook "default port of selected database";
|
defaultText = literalDocBook "default port of selected database";
|
||||||
description = ''
|
description = ''
|
||||||
Port of the database to connect to.
|
Port of the database to connect to.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
useSSL = lib.mkOption {
|
useSSL = mkOption {
|
||||||
type = lib.types.bool;
|
type = bool;
|
||||||
default = cfg.database.host != "localhost";
|
default = cfg.database.host != "localhost";
|
||||||
defaultText = lib.literalExpression ''config.${opt.database.host} != "localhost"'';
|
defaultText = literalExpression ''config.${opt.database.host} != "localhost"'';
|
||||||
description = ''
|
description = ''
|
||||||
Whether the database connection should be secured by SSL /
|
Whether the database connection should be secured by SSL /
|
||||||
TLS.
|
TLS.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
caCert = lib.mkOption {
|
caCert = mkOption {
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = nullOr path;
|
||||||
default = null;
|
default = null;
|
||||||
description = ''
|
description = ''
|
||||||
The SSL / TLS CA certificate that verifies the identity of the
|
The SSL / TLS CA certificate that verifies the identity of the
|
||||||
@ -175,8 +188,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
createLocally = lib.mkOption {
|
createLocally = mkOption {
|
||||||
type = lib.types.bool;
|
type = bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
Whether a database should be automatically created on the
|
Whether a database should be automatically created on the
|
||||||
@ -186,8 +199,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
username = lib.mkOption {
|
username = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "keycloak";
|
default = "keycloak";
|
||||||
description = ''
|
description = ''
|
||||||
Username to use when connecting to an external or manually
|
Username to use when connecting to an external or manually
|
||||||
@ -202,8 +215,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
passwordFile = lib.mkOption {
|
passwordFile = mkOption {
|
||||||
type = lib.types.path;
|
type = path;
|
||||||
example = "/run/keys/db_password";
|
example = "/run/keys/db_password";
|
||||||
description = ''
|
description = ''
|
||||||
File containing the database password.
|
File containing the database password.
|
||||||
@ -214,17 +227,17 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
package = lib.mkOption {
|
package = mkOption {
|
||||||
type = lib.types.package;
|
type = package;
|
||||||
default = pkgs.keycloak;
|
default = pkgs.keycloak;
|
||||||
defaultText = lib.literalExpression "pkgs.keycloak";
|
defaultText = literalExpression "pkgs.keycloak";
|
||||||
description = ''
|
description = ''
|
||||||
Keycloak package to use.
|
Keycloak package to use.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
initialAdminPassword = lib.mkOption {
|
initialAdminPassword = mkOption {
|
||||||
type = lib.types.str;
|
type = str;
|
||||||
default = "changeme";
|
default = "changeme";
|
||||||
description = ''
|
description = ''
|
||||||
Initial password set for the <literal>admin</literal>
|
Initial password set for the <literal>admin</literal>
|
||||||
@ -233,8 +246,8 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
themes = lib.mkOption {
|
themes = mkOption {
|
||||||
type = lib.types.attrsOf lib.types.package;
|
type = attrsOf package;
|
||||||
default = { };
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Additional theme packages for Keycloak. Each theme is linked into
|
Additional theme packages for Keycloak. Each theme is linked into
|
||||||
@ -247,10 +260,10 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
extraConfig = lib.mkOption {
|
extraConfig = mkOption {
|
||||||
type = lib.types.attrsOf lib.types.anything;
|
type = attrsOf anything;
|
||||||
default = { };
|
default = { };
|
||||||
example = lib.literalExpression ''
|
example = literalExpression ''
|
||||||
{
|
{
|
||||||
"subsystem=keycloak-server" = {
|
"subsystem=keycloak-server" = {
|
||||||
"spi=hostname" = {
|
"spi=hostname" = {
|
||||||
@ -332,10 +345,11 @@ in
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: theme: "linkTheme ${theme} ${lib.escapeShellArg name}") cfg.themes)}
|
${concatStringsSep "\n" (mapAttrsToList (name: theme: "linkTheme ${theme} ${escapeShellArg name}") cfg.themes)}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
keycloakConfig' = builtins.foldl' lib.recursiveUpdate {
|
keycloakConfig' = foldl' recursiveUpdate
|
||||||
|
{
|
||||||
"interface=public".inet-address = cfg.bindAddress;
|
"interface=public".inet-address = cfg.bindAddress;
|
||||||
"socket-binding-group=standard-sockets"."socket-binding=http".port = cfg.httpPort;
|
"socket-binding-group=standard-sockets"."socket-binding=http".port = cfg.httpPort;
|
||||||
"subsystem=keycloak-server" = {
|
"subsystem=keycloak-server" = {
|
||||||
@ -353,7 +367,7 @@ in
|
|||||||
password = "@db-password@";
|
password = "@db-password@";
|
||||||
};
|
};
|
||||||
} [
|
} [
|
||||||
(lib.optionalAttrs (cfg.database.type == "postgresql") {
|
(optionalAttrs (cfg.database.type == "postgresql") {
|
||||||
"subsystem=datasources" = {
|
"subsystem=datasources" = {
|
||||||
"jdbc-driver=postgresql" = {
|
"jdbc-driver=postgresql" = {
|
||||||
driver-module-name = "org.postgresql";
|
driver-module-name = "org.postgresql";
|
||||||
@ -361,16 +375,16 @@ in
|
|||||||
driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource";
|
driver-xa-datasource-class-name = "org.postgresql.xa.PGXADataSource";
|
||||||
};
|
};
|
||||||
"data-source=KeycloakDS" = {
|
"data-source=KeycloakDS" = {
|
||||||
connection-url = "jdbc:postgresql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
|
connection-url = "jdbc:postgresql://${cfg.database.host}:${toString cfg.database.port}/keycloak";
|
||||||
driver-name = "postgresql";
|
driver-name = "postgresql";
|
||||||
"connection-properties=ssl".value = lib.boolToString cfg.database.useSSL;
|
"connection-properties=ssl".value = boolToString cfg.database.useSSL;
|
||||||
} // (lib.optionalAttrs (cfg.database.caCert != null) {
|
} // (optionalAttrs (cfg.database.caCert != null) {
|
||||||
"connection-properties=sslrootcert".value = cfg.database.caCert;
|
"connection-properties=sslrootcert".value = cfg.database.caCert;
|
||||||
"connection-properties=sslmode".value = "verify-ca";
|
"connection-properties=sslmode".value = "verify-ca";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.optionalAttrs (cfg.database.type == "mysql") {
|
(optionalAttrs (cfg.database.type == "mysql") {
|
||||||
"subsystem=datasources" = {
|
"subsystem=datasources" = {
|
||||||
"jdbc-driver=mysql" = {
|
"jdbc-driver=mysql" = {
|
||||||
driver-module-name = "com.mysql";
|
driver-module-name = "com.mysql";
|
||||||
@ -378,38 +392,38 @@ in
|
|||||||
driver-class-name = "com.mysql.jdbc.Driver";
|
driver-class-name = "com.mysql.jdbc.Driver";
|
||||||
};
|
};
|
||||||
"data-source=KeycloakDS" = {
|
"data-source=KeycloakDS" = {
|
||||||
connection-url = "jdbc:mysql://${cfg.database.host}:${builtins.toString cfg.database.port}/keycloak";
|
connection-url = "jdbc:mysql://${cfg.database.host}:${toString cfg.database.port}/keycloak";
|
||||||
driver-name = "mysql";
|
driver-name = "mysql";
|
||||||
"connection-properties=useSSL".value = lib.boolToString cfg.database.useSSL;
|
"connection-properties=useSSL".value = boolToString cfg.database.useSSL;
|
||||||
"connection-properties=requireSSL".value = lib.boolToString cfg.database.useSSL;
|
"connection-properties=requireSSL".value = boolToString cfg.database.useSSL;
|
||||||
"connection-properties=verifyServerCertificate".value = lib.boolToString cfg.database.useSSL;
|
"connection-properties=verifyServerCertificate".value = boolToString cfg.database.useSSL;
|
||||||
"connection-properties=characterEncoding".value = "UTF-8";
|
"connection-properties=characterEncoding".value = "UTF-8";
|
||||||
valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker";
|
valid-connection-checker-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker";
|
||||||
validate-on-match = true;
|
validate-on-match = true;
|
||||||
exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter";
|
exception-sorter-class-name = "org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter";
|
||||||
} // (lib.optionalAttrs (cfg.database.caCert != null) {
|
} // (optionalAttrs (cfg.database.caCert != null) {
|
||||||
"connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}";
|
"connection-properties=trustCertificateKeyStoreUrl".value = "file:${mySqlCaKeystore}";
|
||||||
"connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword";
|
"connection-properties=trustCertificateKeyStorePassword".value = "notsosecretpassword";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) {
|
(optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) {
|
||||||
"socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort;
|
"socket-binding-group=standard-sockets"."socket-binding=https".port = cfg.httpsPort;
|
||||||
"subsystem=elytron" = lib.mkOrder 900 {
|
"subsystem=elytron" = mkOrder 900 {
|
||||||
"key-store=httpsKS" = lib.mkOrder 900 {
|
"key-store=httpsKS" = mkOrder 900 {
|
||||||
path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
|
path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
|
||||||
credential-reference.clear-text = "notsosecretpassword";
|
credential-reference.clear-text = "notsosecretpassword";
|
||||||
type = "JKS";
|
type = "JKS";
|
||||||
};
|
};
|
||||||
"key-manager=httpsKM" = lib.mkOrder 901 {
|
"key-manager=httpsKM" = mkOrder 901 {
|
||||||
key-store = "httpsKS";
|
key-store = "httpsKS";
|
||||||
credential-reference.clear-text = "notsosecretpassword";
|
credential-reference.clear-text = "notsosecretpassword";
|
||||||
};
|
};
|
||||||
"server-ssl-context=httpsSSC" = lib.mkOrder 902 {
|
"server-ssl-context=httpsSSC" = mkOrder 902 {
|
||||||
key-manager = "httpsKM";
|
key-manager = "httpsKM";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"subsystem=undertow" = lib.mkOrder 901 {
|
"subsystem=undertow" = mkOrder 901 {
|
||||||
"server=default-server"."https-listener=https".ssl-context = "httpsSSC";
|
"server=default-server"."https-listener=https".ssl-context = "httpsSSC";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@ -500,7 +514,7 @@ in
|
|||||||
# with `expression` to evaluate.
|
# with `expression` to evaluate.
|
||||||
prefixExpression = string:
|
prefixExpression = string:
|
||||||
let
|
let
|
||||||
matchResult = builtins.match ''"\$\{.*}"'' string;
|
matchResult = match ''"\$\{.*}"'' string;
|
||||||
in
|
in
|
||||||
if matchResult != null then
|
if matchResult != null then
|
||||||
"expression " + string
|
"expression " + string
|
||||||
@ -509,30 +523,31 @@ in
|
|||||||
|
|
||||||
writeAttribute = attribute: value:
|
writeAttribute = attribute: value:
|
||||||
let
|
let
|
||||||
type = builtins.typeOf value;
|
type = typeOf value;
|
||||||
in
|
in
|
||||||
if type == "set" then
|
if type == "set" then
|
||||||
let
|
let
|
||||||
names = builtins.attrNames value;
|
names = attrNames value;
|
||||||
in
|
in
|
||||||
builtins.foldl' (text: name: text + (writeAttribute "${attribute}.${name}" value.${name})) "" names
|
foldl' (text: name: text + (writeAttribute "${attribute}.${name}" value.${name})) "" names
|
||||||
else if value == null then ''
|
else if value == null then ''
|
||||||
if (outcome == success) of ${path}:read-attribute(name="${attribute}")
|
if (outcome == success) of ${path}:read-attribute(name="${attribute}")
|
||||||
${path}:undefine-attribute(name="${attribute}")
|
${path}:undefine-attribute(name="${attribute}")
|
||||||
end-if
|
end-if
|
||||||
''
|
''
|
||||||
else if builtins.elem type [ "string" "path" "bool" ] then
|
else if elem type [ "string" "path" "bool" ] then
|
||||||
let
|
let
|
||||||
value' = if type == "bool" then lib.boolToString value else ''"${value}"'';
|
value' = if type == "bool" then boolToString value else ''"${value}"'';
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
if (result != ${prefixExpression value'}) of ${path}:read-attribute(name="${attribute}")
|
if (result != ${prefixExpression value'}) of ${path}:read-attribute(name="${attribute}")
|
||||||
${path}:write-attribute(name=${attribute}, value=${value'})
|
${path}:write-attribute(name=${attribute}, value=${value'})
|
||||||
end-if
|
end-if
|
||||||
''
|
''
|
||||||
else throw "Unsupported type '${type}' for path '${path}'!";
|
else throw "Unsupported type '${type}' for path '${path}'!";
|
||||||
in
|
in
|
||||||
lib.concatStrings
|
concatStrings
|
||||||
(lib.mapAttrsToList
|
(mapAttrsToList
|
||||||
(attribute: value: (writeAttribute attribute value))
|
(attribute: value: (writeAttribute attribute value))
|
||||||
set);
|
set);
|
||||||
|
|
||||||
@ -557,19 +572,19 @@ in
|
|||||||
let
|
let
|
||||||
makeArg = attribute: value:
|
makeArg = attribute: value:
|
||||||
let
|
let
|
||||||
type = builtins.typeOf value;
|
type = typeOf value;
|
||||||
in
|
in
|
||||||
if type == "set" then
|
if type == "set" then
|
||||||
"${attribute} = { " + (makeArgList value) + " }"
|
"${attribute} = { " + (makeArgList value) + " }"
|
||||||
else if builtins.elem type [ "string" "path" "bool" ] then
|
else if elem type [ "string" "path" "bool" ] then
|
||||||
"${attribute} = ${if type == "bool" then lib.boolToString value else ''"${value}"''}"
|
"${attribute} = ${if type == "bool" then boolToString value else ''"${value}"''}"
|
||||||
else if value == null then
|
else if value == null then
|
||||||
""
|
""
|
||||||
else
|
else
|
||||||
throw "Unsupported type '${type}' for attribute '${attribute}'!";
|
throw "Unsupported type '${type}' for attribute '${attribute}'!";
|
||||||
|
|
||||||
in
|
in
|
||||||
lib.concatStringsSep ", " (lib.mapAttrsToList makeArg set);
|
concatStringsSep ", " (mapAttrsToList makeArg set);
|
||||||
|
|
||||||
|
|
||||||
/* Recurses into the `nodeValue` attrset. Only subattrsets that
|
/* Recurses into the `nodeValue` attrset. Only subattrsets that
|
||||||
@ -579,7 +594,7 @@ in
|
|||||||
recurse = nodePath: nodeValue:
|
recurse = nodePath: nodeValue:
|
||||||
let
|
let
|
||||||
nodeContent =
|
nodeContent =
|
||||||
if builtins.isAttrs nodeValue && nodeValue._type or "" == "order" then
|
if isAttrs nodeValue && nodeValue._type or "" == "order" then
|
||||||
nodeValue.content
|
nodeValue.content
|
||||||
else
|
else
|
||||||
nodeValue;
|
nodeValue;
|
||||||
@ -587,21 +602,23 @@ in
|
|||||||
let
|
let
|
||||||
value = nodeContent.${name};
|
value = nodeContent.${name};
|
||||||
in
|
in
|
||||||
if (builtins.match ".*([=]).*" name) == [ "=" ] then
|
if (match ".*([=]).*" name) == [ "=" ] then
|
||||||
if builtins.isAttrs value || value == null then
|
if isAttrs value || value == null then
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
throw "Parsing path '${lib.concatStringsSep "." (nodePath ++ [ name ])}' failed: JBoss attributes cannot contain '='!"
|
throw "Parsing path '${concatStringsSep "." (nodePath ++ [ name ])}' failed: JBoss attributes cannot contain '='!"
|
||||||
else
|
else
|
||||||
false;
|
false;
|
||||||
jbossPath = "/" + lib.concatStringsSep "/" nodePath;
|
jbossPath = "/" + concatStringsSep "/" nodePath;
|
||||||
children = if !builtins.isAttrs nodeContent then {} else nodeContent;
|
children = if !isAttrs nodeContent then { } else nodeContent;
|
||||||
subPaths = builtins.filter isPath (builtins.attrNames children);
|
subPaths = filter isPath (attrNames children);
|
||||||
getPriority = name:
|
getPriority = name:
|
||||||
let value = children.${name};
|
let
|
||||||
in if value._type or "" == "order" then value.priority else 1000;
|
value = children.${name};
|
||||||
orderedSubPaths = lib.sort (a: b: getPriority a < getPriority b) subPaths;
|
in
|
||||||
jbossAttrs = lib.filterAttrs (name: _: !(isPath name)) children;
|
if value._type or "" == "order" then value.priority else 1000;
|
||||||
|
orderedSubPaths = sort (a: b: getPriority a < getPriority b) subPaths;
|
||||||
|
jbossAttrs = filterAttrs (name: _: !(isPath name)) children;
|
||||||
text =
|
text =
|
||||||
if nodeContent != null then
|
if nodeContent != null then
|
||||||
''
|
''
|
||||||
@ -615,15 +632,18 @@ in
|
|||||||
${jbossPath}:remove()
|
${jbossPath}:remove()
|
||||||
end-if
|
end-if
|
||||||
'';
|
'';
|
||||||
in text + lib.concatMapStringsSep "\n" (name: recurse (nodePath ++ [name]) children.${name}) orderedSubPaths;
|
in
|
||||||
|
text + concatMapStringsSep "\n" (name: recurse (nodePath ++ [ name ]) children.${name}) orderedSubPaths;
|
||||||
in
|
in
|
||||||
recurse [ ] attrs;
|
recurse [ ] attrs;
|
||||||
|
|
||||||
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
|
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
|
||||||
|
|
||||||
keycloakConfig = pkgs.runCommand "keycloak-config" {
|
keycloakConfig = pkgs.runCommand "keycloak-config"
|
||||||
|
{
|
||||||
nativeBuildInputs = [ cfg.package ];
|
nativeBuildInputs = [ cfg.package ];
|
||||||
} ''
|
}
|
||||||
|
''
|
||||||
export JBOSS_BASE_DIR="$(pwd -P)";
|
export JBOSS_BASE_DIR="$(pwd -P)";
|
||||||
export JBOSS_MODULEPATH="${cfg.package}/modules";
|
export JBOSS_MODULEPATH="${cfg.package}/modules";
|
||||||
export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log";
|
export JBOSS_LOG_DIR="$JBOSS_BASE_DIR/log";
|
||||||
@ -652,8 +672,8 @@ in
|
|||||||
cp configuration/standalone.xml $out
|
cp configuration/standalone.xml $out
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
lib.mkIf cfg.enable {
|
mkIf cfg.enable
|
||||||
|
{
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
{
|
||||||
assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
|
assertion = (cfg.database.useSSL && cfg.database.type == "postgresql") -> (cfg.database.caCert != null);
|
||||||
@ -663,7 +683,7 @@ in
|
|||||||
|
|
||||||
environment.systemPackages = [ cfg.package ];
|
environment.systemPackages = [ cfg.package ];
|
||||||
|
|
||||||
systemd.services.keycloakPostgreSQLInit = lib.mkIf createLocalPostgreSQL {
|
systemd.services.keycloakPostgreSQLInit = mkIf createLocalPostgreSQL {
|
||||||
after = [ "postgresql.service" ];
|
after = [ "postgresql.service" ];
|
||||||
before = [ "keycloak.service" ];
|
before = [ "keycloak.service" ];
|
||||||
bindsTo = [ "postgresql.service" ];
|
bindsTo = [ "postgresql.service" ];
|
||||||
@ -687,7 +707,7 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.keycloakMySQLInit = lib.mkIf createLocalMySQL {
|
systemd.services.keycloakMySQLInit = mkIf createLocalMySQL {
|
||||||
after = [ "mysql.service" ];
|
after = [ "mysql.service" ];
|
||||||
before = [ "keycloak.service" ];
|
before = [ "keycloak.service" ];
|
||||||
bindsTo = [ "mysql.service" ];
|
bindsTo = [ "mysql.service" ];
|
||||||
@ -714,13 +734,16 @@ in
|
|||||||
let
|
let
|
||||||
databaseServices =
|
databaseServices =
|
||||||
if createLocalPostgreSQL then [
|
if createLocalPostgreSQL then [
|
||||||
"keycloakPostgreSQLInit.service" "postgresql.service"
|
"keycloakPostgreSQLInit.service"
|
||||||
|
"postgresql.service"
|
||||||
]
|
]
|
||||||
else if createLocalMySQL then [
|
else if createLocalMySQL then [
|
||||||
"keycloakMySQLInit.service" "mysql.service"
|
"keycloakMySQLInit.service"
|
||||||
|
"mysql.service"
|
||||||
]
|
]
|
||||||
else [ ];
|
else [ ];
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
after = databaseServices;
|
after = databaseServices;
|
||||||
bindsTo = databaseServices;
|
bindsTo = databaseServices;
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
@ -735,52 +758,16 @@ in
|
|||||||
JBOSS_MODULEPATH = "${cfg.package}/modules";
|
JBOSS_MODULEPATH = "${cfg.package}/modules";
|
||||||
};
|
};
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStartPre = let
|
LoadCredential = [
|
||||||
startPreFullPrivileges = ''
|
"db_password:${cfg.database.passwordFile}"
|
||||||
set -o errexit -o pipefail -o nounset -o errtrace
|
] ++ optionals (cfg.sslCertificate != null && cfg.sslCertificateKey != null) [
|
||||||
shopt -s inherit_errexit
|
"ssl_cert:${cfg.sslCertificate}"
|
||||||
|
"ssl_key:${cfg.sslCertificateKey}"
|
||||||
umask u=rwx,g=,o=
|
|
||||||
|
|
||||||
install -T -m 0400 -o keycloak -g keycloak '${cfg.database.passwordFile}' /run/keycloak/secrets/db_password
|
|
||||||
'' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
|
|
||||||
install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificate}' /run/keycloak/secrets/ssl_cert
|
|
||||||
install -T -m 0400 -o keycloak -g keycloak '${cfg.sslCertificateKey}' /run/keycloak/secrets/ssl_key
|
|
||||||
'';
|
|
||||||
startPre = ''
|
|
||||||
set -o errexit -o pipefail -o nounset -o errtrace
|
|
||||||
shopt -s inherit_errexit
|
|
||||||
|
|
||||||
umask u=rwx,g=,o=
|
|
||||||
|
|
||||||
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
|
|
||||||
add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
|
|
||||||
'' + lib.optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
|
|
||||||
pushd /run/keycloak/ssl/
|
|
||||||
cat /run/keycloak/secrets/ssl_cert <(echo) \
|
|
||||||
/run/keycloak/secrets/ssl_key <(echo) \
|
|
||||||
/etc/ssl/certs/ca-certificates.crt \
|
|
||||||
> allcerts.pem
|
|
||||||
openssl pkcs12 -export -in /run/keycloak/secrets/ssl_cert -inkey /run/keycloak/secrets/ssl_key -chain \
|
|
||||||
-name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
|
|
||||||
-CAfile allcerts.pem -passout pass:notsosecretpassword
|
|
||||||
popd
|
|
||||||
'';
|
|
||||||
in [
|
|
||||||
"+${pkgs.writeShellScript "keycloak-start-pre-full-privileges" startPreFullPrivileges}"
|
|
||||||
"${pkgs.writeShellScript "keycloak-start-pre" startPre}"
|
|
||||||
];
|
];
|
||||||
ExecStart = "${cfg.package}/bin/standalone.sh";
|
|
||||||
User = "keycloak";
|
User = "keycloak";
|
||||||
Group = "keycloak";
|
Group = "keycloak";
|
||||||
DynamicUser = true;
|
DynamicUser = true;
|
||||||
RuntimeDirectory = map (p: "keycloak/" + p) [
|
RuntimeDirectory = map (p: "keycloak/" + p) [
|
||||||
"secrets"
|
|
||||||
"configuration"
|
"configuration"
|
||||||
"deployments"
|
"deployments"
|
||||||
"data"
|
"data"
|
||||||
@ -792,13 +779,39 @@ in
|
|||||||
LogsDirectory = "keycloak";
|
LogsDirectory = "keycloak";
|
||||||
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||||
};
|
};
|
||||||
|
script = ''
|
||||||
|
set -o errexit -o pipefail -o nounset -o errtrace
|
||||||
|
shopt -s inherit_errexit
|
||||||
|
|
||||||
|
umask u=rwx,g=,o=
|
||||||
|
|
||||||
|
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@' "$CREDENTIALS_DIRECTORY/db_password" /run/keycloak/configuration/standalone.xml
|
||||||
|
|
||||||
|
export JAVA_OPTS=-Djboss.server.config.user.dir=/run/keycloak/configuration
|
||||||
|
add-user-keycloak.sh -u admin -p '${cfg.initialAdminPassword}'
|
||||||
|
'' + optionalString (cfg.sslCertificate != null && cfg.sslCertificateKey != null) ''
|
||||||
|
pushd /run/keycloak/ssl/
|
||||||
|
cat "$CREDENTIALS_DIRECTORY/ssl_cert" <(echo) \
|
||||||
|
"$CREDENTIALS_DIRECTORY/ssl_key" <(echo) \
|
||||||
|
/etc/ssl/certs/ca-certificates.crt \
|
||||||
|
> allcerts.pem
|
||||||
|
openssl pkcs12 -export -in "$CREDENTIALS_DIRECTORY/ssl_cert" -inkey "$CREDENTIALS_DIRECTORY/ssl_key" -chain \
|
||||||
|
-name "${cfg.frontendUrl}" -out certificate_private_key_bundle.p12 \
|
||||||
|
-CAfile allcerts.pem -passout pass:notsosecretpassword
|
||||||
|
popd
|
||||||
|
'' + ''
|
||||||
|
${cfg.package}/bin/standalone.sh
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
services.postgresql.enable = lib.mkDefault createLocalPostgreSQL;
|
services.postgresql.enable = mkDefault createLocalPostgreSQL;
|
||||||
services.mysql.enable = lib.mkDefault createLocalMySQL;
|
services.mysql.enable = mkDefault createLocalMySQL;
|
||||||
services.mysql.package = lib.mkIf createLocalMySQL pkgs.mariadb;
|
services.mysql.package = mkIf createLocalMySQL pkgs.mariadb;
|
||||||
};
|
};
|
||||||
|
|
||||||
meta.doc = ./keycloak.xml;
|
meta.doc = ./keycloak.xml;
|
||||||
meta.maintainers = [ lib.maintainers.talyz ];
|
meta.maintainers = [ maintainers.talyz ];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user