Merge pull request #154193 from abbradar/keycloak-changes
keycloak: 15.1.0 -> 16.1.0 + module improvements
This commit is contained in:
commit
cdd600c430
@ -55,7 +55,11 @@ in
|
|||||||
|
|
||||||
frontendUrl = lib.mkOption {
|
frontendUrl = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
apply = x: if lib.hasSuffix "/" x then x else x + "/";
|
apply = x:
|
||||||
|
if x == "" || lib.hasSuffix "/" x then
|
||||||
|
x
|
||||||
|
else
|
||||||
|
x + "/";
|
||||||
example = "keycloak.example.com/auth";
|
example = "keycloak.example.com/auth";
|
||||||
description = ''
|
description = ''
|
||||||
The public URL used as base for all frontend requests. Should
|
The public URL used as base for all frontend requests. Should
|
||||||
@ -229,8 +233,22 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
themes = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.package;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Additional theme packages for Keycloak. Each theme is linked into
|
||||||
|
subdirectory with a corresponding attribute name.
|
||||||
|
|
||||||
|
Theme packages consist of several subdirectories which provide
|
||||||
|
different theme types: for example, <literal>account</literal>,
|
||||||
|
<literal>login</literal> etc. After adding a theme to this option you
|
||||||
|
can select it by its name in Keycloak administration console.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
extraConfig = lib.mkOption {
|
extraConfig = lib.mkOption {
|
||||||
type = lib.types.attrs;
|
type = lib.types.attrsOf lib.types.anything;
|
||||||
default = { };
|
default = { };
|
||||||
example = lib.literalExpression ''
|
example = lib.literalExpression ''
|
||||||
{
|
{
|
||||||
@ -289,16 +307,45 @@ in
|
|||||||
${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
|
${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Both theme and theme type directories need to be actual directories in one hierarchy to pass Keycloak checks.
|
||||||
|
themesBundle = pkgs.runCommand "keycloak-themes" {} ''
|
||||||
|
linkTheme() {
|
||||||
|
theme="$1"
|
||||||
|
name="$2"
|
||||||
|
|
||||||
|
mkdir "$out/$name"
|
||||||
|
for typeDir in "$theme"/*; do
|
||||||
|
if [ -d "$typeDir" ]; then
|
||||||
|
type="$(basename "$typeDir")"
|
||||||
|
mkdir "$out/$name/$type"
|
||||||
|
for file in "$typeDir"/*; do
|
||||||
|
ln -sn "$file" "$out/$name/$type/$(basename "$file")"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$out"
|
||||||
|
for theme in ${cfg.package}/themes/*; do
|
||||||
|
if [ -d "$theme" ]; then
|
||||||
|
linkTheme "$theme" "$(basename "$theme")"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: theme: "linkTheme ${theme} ${lib.escapeShellArg name}") cfg.themes)}
|
||||||
|
'';
|
||||||
|
|
||||||
keycloakConfig' = builtins.foldl' lib.recursiveUpdate {
|
keycloakConfig' = builtins.foldl' lib.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"."spi=hostname" = {
|
"subsystem=keycloak-server" = {
|
||||||
"provider=default" = {
|
"spi=hostname"."provider=default" = {
|
||||||
enabled = true;
|
enabled = true;
|
||||||
properties = {
|
properties = {
|
||||||
inherit (cfg) frontendUrl forceBackendUrlToFrontendUrl;
|
inherit (cfg) frontendUrl forceBackendUrlToFrontendUrl;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
"theme=defaults".dir = toString themesBundle;
|
||||||
};
|
};
|
||||||
"subsystem=datasources"."data-source=KeycloakDS" = {
|
"subsystem=datasources"."data-source=KeycloakDS" = {
|
||||||
max-pool-size = "20";
|
max-pool-size = "20";
|
||||||
@ -348,11 +395,23 @@ in
|
|||||||
})
|
})
|
||||||
(lib.optionalAttrs (cfg.sslCertificate != null && cfg.sslCertificateKey != null) {
|
(lib.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;
|
||||||
"core-service=management"."security-realm=UndertowRealm"."server-identity=ssl" = {
|
"subsystem=elytron" = lib.mkOrder 900 {
|
||||||
keystore-path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
|
"key-store=httpsKS" = lib.mkOrder 900 {
|
||||||
keystore-password = "notsosecretpassword";
|
path = "/run/keycloak/ssl/certificate_private_key_bundle.p12";
|
||||||
|
credential-reference.clear-text = "notsosecretpassword";
|
||||||
|
type = "JKS";
|
||||||
|
};
|
||||||
|
"key-manager=httpsKM" = lib.mkOrder 901 {
|
||||||
|
key-store = "httpsKS";
|
||||||
|
credential-reference.clear-text = "notsosecretpassword";
|
||||||
|
};
|
||||||
|
"server-ssl-context=httpsSSC" = lib.mkOrder 902 {
|
||||||
|
key-manager = "httpsKM";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"subsystem=undertow" = lib.mkOrder 901 {
|
||||||
|
"server=default-server"."https-listener=https".ssl-context = "httpsSSC";
|
||||||
};
|
};
|
||||||
"subsystem=undertow"."server=default-server"."https-listener=https".security-realm = "UndertowRealm";
|
|
||||||
})
|
})
|
||||||
cfg.extraConfig
|
cfg.extraConfig
|
||||||
];
|
];
|
||||||
@ -441,9 +500,9 @@ in
|
|||||||
# with `expression` to evaluate.
|
# with `expression` to evaluate.
|
||||||
prefixExpression = string:
|
prefixExpression = string:
|
||||||
let
|
let
|
||||||
match = (builtins.match ''"\$\{.*}"'' string);
|
matchResult = builtins.match ''"\$\{.*}"'' string;
|
||||||
in
|
in
|
||||||
if match != null then
|
if matchResult != null then
|
||||||
"expression " + string
|
"expression " + string
|
||||||
else
|
else
|
||||||
string;
|
string;
|
||||||
@ -508,52 +567,57 @@ in
|
|||||||
""
|
""
|
||||||
else
|
else
|
||||||
throw "Unsupported type '${type}' for attribute '${attribute}'!";
|
throw "Unsupported type '${type}' for attribute '${attribute}'!";
|
||||||
|
|
||||||
in
|
in
|
||||||
lib.concatStringsSep ", " (lib.mapAttrsToList makeArg set);
|
lib.concatStringsSep ", " (lib.mapAttrsToList makeArg set);
|
||||||
|
|
||||||
|
|
||||||
/* Recurses into the `attrs` attrset, beginning at the path
|
/* Recurses into the `nodeValue` attrset. Only subattrsets that
|
||||||
resolved from `state.path ++ node`; if `node` is `null`,
|
are JBoss paths, i.e. follows the `key=value` format, are recursed
|
||||||
starts from `state.path`. Only subattrsets that are JBoss
|
|
||||||
paths, i.e. follows the `key=value` format, are recursed
|
|
||||||
into - the rest are considered JBoss attributes / maps.
|
into - the rest are considered JBoss attributes / maps.
|
||||||
*/
|
*/
|
||||||
recurse = state: node:
|
recurse = nodePath: nodeValue:
|
||||||
let
|
let
|
||||||
path = state.path ++ (lib.optional (node != null) node);
|
nodeContent =
|
||||||
|
if builtins.isAttrs nodeValue && nodeValue._type or "" == "order" then
|
||||||
|
nodeValue.content
|
||||||
|
else
|
||||||
|
nodeValue;
|
||||||
isPath = name:
|
isPath = name:
|
||||||
let
|
let
|
||||||
value = lib.getAttrFromPath (path ++ [ name ]) attrs;
|
value = nodeContent.${name};
|
||||||
in
|
in
|
||||||
if (builtins.match ".*([=]).*" name) == [ "=" ] then
|
if (builtins.match ".*([=]).*" name) == [ "=" ] then
|
||||||
if builtins.isAttrs value || value == null then
|
if builtins.isAttrs value || value == null then
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
throw "Parsing path '${lib.concatStringsSep "." (path ++ [ name ])}' failed: JBoss attributes cannot contain '='!"
|
throw "Parsing path '${lib.concatStringsSep "." (nodePath ++ [ name ])}' failed: JBoss attributes cannot contain '='!"
|
||||||
else
|
else
|
||||||
false;
|
false;
|
||||||
jbossPath = "/" + (lib.concatStringsSep "/" path);
|
jbossPath = "/" + lib.concatStringsSep "/" nodePath;
|
||||||
nodeValue = lib.getAttrFromPath path attrs;
|
children = if !builtins.isAttrs nodeContent then {} else nodeContent;
|
||||||
children = if !builtins.isAttrs nodeValue then {} else nodeValue;
|
|
||||||
subPaths = builtins.filter isPath (builtins.attrNames children);
|
subPaths = builtins.filter isPath (builtins.attrNames children);
|
||||||
|
getPriority = name:
|
||||||
|
let value = children.${name};
|
||||||
|
in if value._type or "" == "order" then value.priority else 1000;
|
||||||
|
orderedSubPaths = lib.sort (a: b: getPriority a < getPriority b) subPaths;
|
||||||
jbossAttrs = lib.filterAttrs (name: _: !(isPath name)) children;
|
jbossAttrs = lib.filterAttrs (name: _: !(isPath name)) children;
|
||||||
in
|
text =
|
||||||
state // {
|
if nodeContent != null then
|
||||||
text = state.text + (
|
''
|
||||||
if nodeValue != null then ''
|
|
||||||
if (outcome != success) of ${jbossPath}:read-resource()
|
if (outcome != success) of ${jbossPath}:read-resource()
|
||||||
${jbossPath}:add(${makeArgList jbossAttrs})
|
${jbossPath}:add(${makeArgList jbossAttrs})
|
||||||
end-if
|
end-if
|
||||||
'' + (writeAttributes jbossPath jbossAttrs)
|
'' + writeAttributes jbossPath jbossAttrs
|
||||||
else ''
|
else
|
||||||
|
''
|
||||||
if (outcome == success) of ${jbossPath}:read-resource()
|
if (outcome == success) of ${jbossPath}:read-resource()
|
||||||
${jbossPath}:remove()
|
${jbossPath}:remove()
|
||||||
end-if
|
end-if
|
||||||
'') + (builtins.foldl' recurse { text = ""; inherit path; } subPaths).text;
|
'';
|
||||||
};
|
in text + lib.concatMapStringsSep "\n" (name: recurse (nodePath ++ [name]) children.${name}) orderedSubPaths;
|
||||||
in
|
in
|
||||||
(recurse { text = ""; path = []; } null).text;
|
recurse [] attrs;
|
||||||
|
|
||||||
|
|
||||||
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
|
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
|
||||||
|
|
||||||
|
@ -85,7 +85,12 @@
|
|||||||
The frontend URL is used as base for all frontend requests and
|
The frontend URL is used as base for all frontend requests and
|
||||||
must be configured through <xref linkend="opt-services.keycloak.frontendUrl" />.
|
must be configured through <xref linkend="opt-services.keycloak.frontendUrl" />.
|
||||||
It should normally include a trailing <literal>/auth</literal>
|
It should normally include a trailing <literal>/auth</literal>
|
||||||
(the default web context).
|
(the default web context). If you use a reverse proxy, you need
|
||||||
|
to set this option to <literal>""</literal>, so that frontend URL
|
||||||
|
is derived from HTTP headers. <literal>X-Forwarded-*</literal> headers
|
||||||
|
support also should be enabled, using <link
|
||||||
|
xlink:href="https://www.keycloak.org/docs/latest/server_installation/index.html#identifying-client-ip-addresses">
|
||||||
|
respective guidelines</link>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -131,6 +136,17 @@
|
|||||||
</warning>
|
</warning>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="module-services-keycloak-themes">
|
||||||
|
<title>Themes</title>
|
||||||
|
<para>
|
||||||
|
You can package custom themes and make them visible to Keycloak via
|
||||||
|
<xref linkend="opt-services.keycloak.themes" />
|
||||||
|
option. See the <link xlink:href="https://www.keycloak.org/docs/latest/server_development/#_themes">
|
||||||
|
Themes section of the Keycloak Server Development Guide</link>
|
||||||
|
and respective NixOS option description for more information.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section xml:id="module-services-keycloak-extra-config">
|
<section xml:id="module-services-keycloak-extra-config">
|
||||||
<title>Additional configuration</title>
|
<title>Additional configuration</title>
|
||||||
<para>
|
<para>
|
||||||
|
@ -18,11 +18,11 @@ let
|
|||||||
in
|
in
|
||||||
stdenv.mkDerivation rec {
|
stdenv.mkDerivation rec {
|
||||||
pname = "keycloak";
|
pname = "keycloak";
|
||||||
version = "15.1.0";
|
version = "16.1.0";
|
||||||
|
|
||||||
src = fetchzip {
|
src = fetchzip {
|
||||||
url = "https://github.com/keycloak/keycloak/releases/download/${version}/keycloak-${version}.zip";
|
url = "https://github.com/keycloak/keycloak/releases/download/${version}/keycloak-${version}.zip";
|
||||||
sha256 = "0s8nvp1ca30569k1a7glbn2zvvchz35s2r8d08fbs5zjngnz3276";
|
sha256 = "sha256-QVFu3f+mwafoNUttLEVMdoZHMJjjH/TpZAGV7ZvIvh0=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs = [ makeWrapper ];
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
Loading…
Reference in New Issue
Block a user