feat(keycloak): extraVolumes/extraVolumeMounts for custom themes and providers (1.1.0+up26.8.0)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 11s
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Build keycloak (pull_request) Successful in 4s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 17s
Install-test / Install-test keycloak (pull_request) Successful in 3m9s
Validate Helm Charts / Install-test (pull_request) Successful in 3m9s

This commit is contained in:
jklein committed 2026-10-06 10:26:13 +00:00
1 parent b39f36ff5c
commit d8b392bf64
6 files changed
+214 -2

No files matched your search

+1 -1
View File
@@ -43,7 +43,7 @@ type: application
# procedure lives in NOTES.txt and on the wiki page instead of in a
# packageRules entry. Read the "MINOR UPGRADES" section of NOTES.txt before
# merging an app-version PR that changes the minor.
version: 1.0.0+up26.8.0
version: 1.1.0+up26.8.0
appVersion: "26.8.0"
maintainers:
@@ -146,6 +146,23 @@ spec:
mountPath: {{ include "keycloak.dataDir" . }}
- name: tmp
mountPath: /tmp
{{- with .Values.keycloak.extraVolumeMounts }}
# THE EXTRA MOUNTS, HERE AS WELL AS ON THE SERVING CONTAINER, AND
# THIS OCCURRENCE IS THE LOAD-BEARING ONE (Gitea #88). Do not
# "clean it up" to the serving container only: an SPI provider JAR
# is a BUILD-time concern. Upstream, on adding one: "After that if
# you are using `--optimized`, you must run the `build` command in
# order to update the server's provider registry with the
# implementations from the JAR file." That build is THIS container,
# so a JAR mounted only below is absent while the registry is
# written - it never gets registered and nothing reports it.
#
# One list for both places on purpose: two lists would be two
# chances to mount an extension in the wrong one, and the wrong one
# fails silently. The price is that every extra volume is present at
# build time too, which values.yaml names at the value.
{{- toYaml . | nindent 12 }}
{{- end }}
containers:
- name: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
# Fully qualified: an unqualified reference would resolve to Docker
@@ -358,6 +375,13 @@ spec:
mountPath: {{ include "keycloak.dataDir" . }}
- name: tmp
mountPath: /tmp
{{- with .Values.keycloak.extraVolumeMounts }}
# The extra mounts, second of the two places (see the build init
# container above). This is where a login THEME is read - themes are
# a runtime concern, so one mounted here takes effect even though
# this container serves a pre-built installation.
{{- toYaml . | nindent 12 }}
{{- end }}
# DELIBERATELY NOT MOUNTED: /opt/keycloak/conf. It is not empty in
# the image - it carries keycloak.conf and, more importantly,
# cache-ispn.xml, which is the Infinispan configuration cache=ispn
@@ -371,3 +395,11 @@ spec:
emptyDir: {}
- name: tmp
emptyDir: {}
{{- with .Values.keycloak.extraVolumes }}
# Verbatim, because a volume source can be any of the Kubernetes forms
# and the chart interprets none of them. The one path rule that matters
# is about the MOUNT and not the volume: never /opt/keycloak/themes
# itself, which would hide the image's base, keycloak and keycloak.v2
# themes - see values.yaml at keycloak.extraVolumes.
{{- toYaml . | nindent 8 }}
{{- end }}
+9 -1
View File
@@ -143,7 +143,15 @@
"minAvailable": { "type": "integer", "minimum": 1 }
}
},
"env": { "$ref": "#/definitions/env" }
"env": { "$ref": "#/definitions/env" },
"extraVolumes": {
"description": "Additional pod volumes, rendered verbatim into the pod spec. The ITEMS stay loosely typed on purpose - a volume source can be any of the Kubernetes forms (configMap, secret, persistentVolumeClaim, emptyDir, projected, csi, ...), and a stricter schema here would reject valid configurations rather than catch mistakes; the API server validates the shape anyway. Declaring the key itself is not optional, because the levels above carry additionalProperties: false. Places files, configures nothing: an extra root CA additionally needs KC_TRUSTSTORE_PATHS (a runtime option, so keycloak.env) and a theme additionally needs to be selected per realm in Keycloak's database.",
"type": ["array", "null"]
},
"extraVolumeMounts": {
"description": "Additional volume mounts, rendered verbatim and TWICE - on the build init container as well as on the serving container (Gitea #88). That is deliberate: a login theme is read at runtime, but an SPI provider JAR must be present while `kc.sh build` writes the provider registry, or it is never registered and nothing reports it. Items loosely typed for the same reason as extraVolumes. Never mount on /opt/keycloak/themes itself - it would hide the image's base, keycloak and keycloak.v2 themes; use a subpath /opt/keycloak/themes/<name>.",
"type": ["array", "null"]
}
}
},
"ingress": {
+105
View File
@@ -509,6 +509,111 @@ keycloak:
# KC_SPI_* provider settings.
env: []
# ADDITIONAL VOLUMES AND THEIR MOUNTS - the way a file gets into this pod at
# all (Gitea #88). Deliberately GENERIC rather than a theme-specific key:
# the same pair serves a custom login theme, an SPI provider JAR and an extra
# root CA, and the chart does not have to decide how a theme is delivered
# (ConfigMap, Secret, PVC, an init container of your own filling an
# emptyDir). Both lists are passed through verbatim, so an entry may use any
# Kubernetes volume source.
#
# THE MOUNTS ARE RENDERED TWICE - ON THE build INIT CONTAINER AND ON THE
# SERVING CONTAINER - AND THAT IS THE POINT, NOT AN OVERSIGHT. One list, two
# places, because the two cases are resolved at different times:
#
# * The SERVING container is where a THEME is read. Themes are a runtime
# concern, so a theme mounted there takes effect in the running server
# even though this chart serves a pre-built installation.
# * The build INIT container is where `kc.sh build` runs (point 2 at the
# top of this file), and a PROVIDER is a build-time concern. Upstream, on
# dropping a provider JAR into providers/: "After that if you are using
# `--optimized`, you must run the `build` command in order to update the
# server's provider registry with the implementations from the JAR file."
# This chart runs `start --optimized`, so a JAR that is mounted only on
# the serving container is absent while the registry is written: it never
# gets registered, the file sits there at runtime, and NOTHING reports
# it - no error, no warning, just an extension that does nothing.
#
# Not on the SEED init container: it has a different layout (it only writes
# the shared volume at /quarkus-build and runs no Keycloak command), so a
# mount there would be pointless.
#
# The side effect of the second place, said out loud rather than discovered:
# an extra volume is present AT BUILD TIME as well. For a theme that is
# without consequence. For a Secret it means an init container of this pod
# sees it too - the same pod either way, but it belongs stated.
#
# WHAT THIS PAIR DOES NOT DO: it places files, it does not configure
# Keycloak. Two cases where that difference is the whole thing:
#
# * AN EXTRA ROOT CA IS NOT TRUSTED BY BEING MOUNTED. A file in the pod is
# in no truststore; Keycloak reads one only from truststore-paths, so the
# mount has to be accompanied by KC_TRUSTSTORE_PATHS pointing at the
# mounted DIRECTORY. That is a RUNTIME option (checked against the 26.8.0
# option table, where it carries no build icon), so keycloak.env above is
# the right place for it and the build-option guard does not refuse it.
# * A THEME IS NOT ACTIVATED BY BEING MOUNTED, AND THIS CHART CANNOT
# ACTIVATE IT. The selection lives PER REALM in Keycloak's database
# (Realm settings -> Themes -> Login theme). Mounting makes a theme
# selectable, never selected - so do not go looking for a bug when a
# freshly mounted theme changes nothing.
#
# THE THEME CASE, AND THE ONE MISTAKE EVERYBODY MAKES EXACTLY ONCE:
#
# DO NOT MOUNT ON /opt/keycloak/themes. The image's own themes live there
# (base, keycloak, keycloak.v2) and a volume at that path HIDES ALL THREE -
# the login page is then broken rather than merely unstyled. Mount on a
# SUBPATH that does not exist in the image, /opt/keycloak/themes/<name>,
# which hides nothing. Same mechanism and same class of mistake as the
# "DELIBERATELY NOT MOUNTED: /opt/keycloak/conf" note in the Deployment,
# where an emptyDir would hide the cache-ispn.xml that cache=ispn reads.
#
# extraVolumes:
# - name: login-theme
# configMap:
# name: my-login-theme
# # A theme's own layout is <theme>/<type>/theme.properties, and a
# # ConfigMap key may not contain a slash - so the subdirectory comes
# # from items[].path, which may.
# items:
# - key: theme.properties
# path: login/theme.properties
# extraVolumeMounts:
# - name: login-theme
# # NOT /opt/keycloak/themes - see above.
# mountPath: /opt/keycloak/themes/my-theme
# readOnly: true
#
# With that in place a theme.properties carrying
#
# parent=keycloak.v2
# kcLogoIdP-<alias> = fa fa-lock
#
# is what puts an icon on an identity provider's login button - Keycloak has
# no logo field on a provider, so a login theme is the only way to set one.
#
# FOUR PROPERTIES OF THE THEME ROUTE THAT COST TIME IF THEY ARE NOT KNOWN:
#
# 1. A ConfigMap carries binary content ONLY in binaryData (base64). An SVG
# is text and fits in data; a PNG does not. And the 1 MiB limit per
# ConfigMap applies - ample for a theme with SVG icons, not enough for
# bundled web fonts, which belong in a PVC or an image of your own.
# 2. THEMES ARE CACHED, and in production that is wanted. It means a theme
# change takes effect only after a pod restart. Two things add up here:
# this chart hashes no user-supplied ConfigMap either (it renders none of
# its own - point 1 at the top of this file), so editing the theme
# ConfigMap restarts nothing by itself. Use
# `kubectl rollout restart deploy/<release>-keycloak-deployment`.
# Do not configure the cache away to avoid this.
# 3. The realm selection is not ours to make - see above.
# 4. A CUSTOM LOGIN THEME CAN MAKE THE IDENTITY-PROVIDER BUTTONS DISAPPEAR
# ENTIRELY when its template does not include the social fragment
# correctly (reported repeatedly upstream, keycloak/keycloak#42711).
# After switching a realm's theme, LOOK AT THE LOGIN PAGE - a healthy
# pod says nothing about whether the buttons are still there.
extraVolumes: []
extraVolumeMounts: []
ingress:
# The PUBLIC ingress - the login endpoint, the token endpoints, the account
# console. On by default, because an identity provider nothing can reach is