summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKim Lindberger <kim.lindberger@gmail.com>2022-01-17 11:45:46 +0100
committerGitHub <noreply@github.com>2022-01-17 11:45:46 +0100
commitb28d5bc405694cfc100811c62dc01cae8c8d6137 (patch)
tree374c25843e2e5cec71063ed09bac09b677fc958f
parentMerge pull request #155271 from blitz/tailscale-update (diff)
parentkeycloak service: allow to set empty frontend URL (diff)
downloadnixpkgs-b28d5bc405694cfc100811c62dc01cae8c8d6137.tar.gz
Merge pull request #155201 from NixOS/backport-154193-to-release-21.11
[Backport release-21.11] keycloak: 15.1.0 -> 16.1.0 + module improvements
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix126
-rw-r--r--nixos/modules/services/web-apps/keycloak.xml18
-rw-r--r--pkgs/servers/keycloak/default.nix4
3 files changed, 114 insertions, 34 deletions
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index df8c7114102f..32c6f35e81c5 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -54,7 +54,11 @@ in
frontendUrl = lib.mkOption {
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";
description = ''
The public URL used as base for all frontend requests. Should
@@ -226,8 +230,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 {
- type = lib.types.attrs;
+ type = lib.types.attrsOf lib.types.anything;
default = { };
example = lib.literalExpression ''
{
@@ -286,16 +304,45 @@ in
${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 {
"interface=public".inet-address = cfg.bindAddress;
"socket-binding-group=standard-sockets"."socket-binding=http".port = cfg.httpPort;
- "subsystem=keycloak-server"."spi=hostname" = {
- "provider=default" = {
+ "subsystem=keycloak-server" = {
+ "spi=hostname"."provider=default" = {
enabled = true;
properties = {
inherit (cfg) frontendUrl forceBackendUrlToFrontendUrl;
};
};
+ "theme=defaults".dir = toString themesBundle;
};
"subsystem=datasources"."data-source=KeycloakDS" = {
max-pool-size = "20";
@@ -345,11 +392,23 @@ in
})
(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";
- keystore-password = "notsosecretpassword";
+ "subsystem=elytron" = lib.mkOrder 900 {
+ "key-store=httpsKS" = lib.mkOrder 900 {
+ 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
];
@@ -438,9 +497,9 @@ in
# with `expression` to evaluate.
prefixExpression = string:
let
- match = (builtins.match ''"\$\{.*}"'' string);
+ matchResult = builtins.match ''"\$\{.*}"'' string;
in
- if match != null then
+ if matchResult != null then
"expression " + string
else
string;
@@ -505,52 +564,57 @@ in
""
else
throw "Unsupported type '${type}' for attribute '${attribute}'!";
+
in
lib.concatStringsSep ", " (lib.mapAttrsToList makeArg set);
- /* Recurses into the `attrs` attrset, beginning at the path
- resolved from `state.path ++ node`; if `node` is `null`,
- starts from `state.path`. Only subattrsets that are JBoss
- paths, i.e. follows the `key=value` format, are recursed
+ /* Recurses into the `nodeValue` attrset. Only subattrsets that
+ are JBoss paths, i.e. follows the `key=value` format, are recursed
into - the rest are considered JBoss attributes / maps.
*/
- recurse = state: node:
+ recurse = nodePath: nodeValue:
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:
let
- value = lib.getAttrFromPath (path ++ [ name ]) attrs;
+ value = nodeContent.${name};
in
if (builtins.match ".*([=]).*" name) == [ "=" ] then
if builtins.isAttrs value || value == null then
true
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
false;
- jbossPath = "/" + (lib.concatStringsSep "/" path);
- nodeValue = lib.getAttrFromPath path attrs;
- children = if !builtins.isAttrs nodeValue then {} else nodeValue;
+ jbossPath = "/" + lib.concatStringsSep "/" nodePath;
+ children = if !builtins.isAttrs nodeContent then {} else nodeContent;
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;
- in
- state // {
- text = state.text + (
- if nodeValue != null then ''
+ text =
+ if nodeContent != null then
+ ''
if (outcome != success) of ${jbossPath}:read-resource()
${jbossPath}:add(${makeArgList jbossAttrs})
end-if
- '' + (writeAttributes jbossPath jbossAttrs)
- else ''
+ '' + writeAttributes jbossPath jbossAttrs
+ else
+ ''
if (outcome == success) of ${jbossPath}:read-resource()
${jbossPath}:remove()
end-if
- '') + (builtins.foldl' recurse { text = ""; inherit path; } subPaths).text;
- };
+ '';
+ in text + lib.concatMapStringsSep "\n" (name: recurse (nodePath ++ [name]) children.${name}) orderedSubPaths;
in
- (recurse { text = ""; path = []; } null).text;
-
+ recurse [] attrs;
jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
diff --git a/nixos/modules/services/web-apps/keycloak.xml b/nixos/modules/services/web-apps/keycloak.xml
index 7ba656c20f16..cb706932f48f 100644
--- a/nixos/modules/services/web-apps/keycloak.xml
+++ b/nixos/modules/services/web-apps/keycloak.xml
@@ -85,7 +85,12 @@
The frontend URL is used as base for all frontend requests and
must be configured through <xref linkend="opt-services.keycloak.frontendUrl" />.
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>
@@ -131,6 +136,17 @@
</warning>
</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">
<title>Additional configuration</title>
<para>
diff --git a/pkgs/servers/keycloak/default.nix b/pkgs/servers/keycloak/default.nix
index ef168272fe76..9d8a2b31bf12 100644
--- a/pkgs/servers/keycloak/default.nix
+++ b/pkgs/servers/keycloak/default.nix
@@ -18,11 +18,11 @@ let
in
stdenv.mkDerivation rec {
pname = "keycloak";
- version = "15.1.0";
+ version = "16.1.0";
src = fetchzip {
url = "https://github.com/keycloak/keycloak/releases/download/${version}/keycloak-${version}.zip";
- sha256 = "0s8nvp1ca30569k1a7glbn2zvvchz35s2r8d08fbs5zjngnz3276";
+ sha256 = "sha256-QVFu3f+mwafoNUttLEVMdoZHMJjjH/TpZAGV7ZvIvh0=";
};
nativeBuildInputs = [ makeWrapper ];