feat(keycloak): one hostname per realm, in both routing modes
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build keycloak (pull_request) Successful in 8s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 24s
Install-test / Install-test keycloak (pull_request) Successful in 3m29s
Validate Helm Charts / Install-test (pull_request) Successful in 3m29s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build keycloak (pull_request) Successful in 8s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 24s
Install-test / Install-test keycloak (pull_request) Successful in 3m29s
Validate Helm Charts / Install-test (pull_request) Successful in 3m29s
keycloak.realmHosts gives a realm a hostname of its own: this release's Service published under a further name, with that name's bare / sent to /realms/<realm>/account/. It renders in BOTH modes - one Ingress per entry carrying nginx.ingress.kubernetes.io/app-root, or one HTTPRoute per entry whose Exact match on / carries a RequestRedirect filter - because a routing value in this repository is mode-independent by design - ingress.domain is read in Gateway mode too - so a feature rendering in one mode only would silently disappear when an installation switched. Default []: nothing renders, and the render is byte-identical to 1.2.0 for the fixture's values in both modes. Minor, additive, no value becomes required. ONE OBJECT PER ENTRY, not one with several hostnames. spec.hostnames applies to the whole object and a rule's matches are path-based, so inside a shared route every entry would match Exact: / and the realm could not be told apart. Names therefore carry the LIST INDEX, as homeassistant's per-host routes do and for the same reason - a realm name is not a dependable object-name fragment. The consequence is written down rather than left to be discovered: reordering the list renames and recreates the objects. THE WORK IS IN THE INGRESS HALF, NOT THE ROUTE. Two convention rules do not survive these objects, and both deviations are required rather than tolerated: tlsSecretName is a per-entry value with NO default, because these hostnames already have certificates and several entries typically share one, so a derived name would ask cert-manager for a second certificate for a name a wildcard already covers; and the cert-manager annotation is OPTIONAL per entry, because cert-manager keys a Certificate by the secret it writes and only one object per secret may request it - the same deliberate omission the qBittorrent object in freesailarr makes. Annotations are per entry too: of the four hostnames this was written for, three carry the proxy-buffer pair and one does not, which a chart-global block could not express. statusCode: 302 is written out at the field's own default. 301 and 302 are both Support: Core, so the portability trap of #119 - an implementation declining an Extended code sets Accepted: False on the WHOLE route - does not apply here; ingress-nginx emits 302 for app-root, and a value that is only accidentally right documents nothing. Five render aborts, each against a failure that reports nothing: an entry without a host (ingress-nginx reads an empty host as a catch-all for the whole controller), without a realm (/realms//account/, a 404 behind a healthy hostname and certificate), without a tlsSecretName in Ingress mode (the controller's default certificate, a browser-only failure), a repeated host or one the release already publishes (webhook rejection in Ingress mode, two merged routes decided by object age in Gateway mode), and a filled list with both modes off (no object at all, and nothing in the release that reveals the list was ignored). The entry-shape checks sit in a shared helper so they exist in both modes, and the Ingress template skips them while httpRoute.enabled is also true so that the specific message cannot hide the mutual-exclusion one. CI: the two entries live in ci/keycloak/test-values.yaml, which ci/httproute/keycloak.yaml is applied on top of, so one definition covers both modes - two Ingress objects in the install-test, two HTTPRoutes in the render test. They cover both Ingress forms, with and without a clusterIssuer, and the render test holds their inertness in Gateway mode. The httproute match test goes from 56 assertions over 21 charts to 60, keycloak's share from 4 to 8 plus two "not checked" lines for the backend-less redirect rules. The #118 prefix/redirect pairing correctly does not fire: it is scoped to rules that strip a prefix, and a realm route strips nothing. NOT IN THIS CHANGE: the migration. Nothing in jk-cluster-config is touched. With the default [] no running system changes, so the order a takeover has to run in is documented instead - remove the objects in the owning repository first, then install with the list filled, because the reverse or both at once is rejected by ingress-nginx's admission webhook and leaves a Fleet bundle in ErrApplied until forceSyncGeneration is raised. Whether anything there depends on those objects BY NAME is unchecked and named as unchecked. Fixes #122
This commit is contained in:
10 files changed
+629
-16
No files matched your search
@@ -614,6 +614,120 @@ keycloak:
|
||||
extraVolumes: []
|
||||
extraVolumeMounts: []
|
||||
|
||||
# ONE HOSTNAME PER REALM (Gitea #122). Empty by default, and an empty list
|
||||
# renders NOTHING in either mode - the default render is byte-identical to a
|
||||
# release without this feature.
|
||||
#
|
||||
# WHAT IT IS FOR. Keycloak serves every realm from this one deployment, and a
|
||||
# realm's account console lives at /realms/<realm>/account/. An entry here
|
||||
# gives one realm a hostname of its own: this release's Service published
|
||||
# under a further name, with the bare / sent to that realm's console and
|
||||
# everything else proxied through unchanged. It renders in BOTH routing modes
|
||||
# - one Ingress per entry with nginx.ingress.kubernetes.io/app-root, or one
|
||||
# HTTPRoute per entry whose Exact match on / carries a RequestRedirect filter.
|
||||
#
|
||||
# ONE OBJECT PER ENTRY, never one object with several hostnames: an
|
||||
# HTTPRoute's hostnames apply to the whole object and a rule's matches are
|
||||
# path-based, so inside a shared route every entry would match `Exact: /` and
|
||||
# the realm could not be told apart. The objects are named by LIST INDEX
|
||||
# (<release>-keycloak-realm-ingress, -realm-ingress-1, ... and the same for
|
||||
# -realm-httproute), which means REORDERING THIS LIST RENAMES THEM and they
|
||||
# are recreated rather than edited. NOTES.txt prints the index-to-hostname
|
||||
# mapping, because the object name does not carry it.
|
||||
#
|
||||
# THE REALM IS NOT DERIVABLE FROM THE HOSTNAME and the chart does not try:
|
||||
# both are given per entry. In the installation this feature was written for,
|
||||
# one of the four hostnames serves a realm whose name shares nothing with it.
|
||||
#
|
||||
# Fields, with the mode that reads each one:
|
||||
#
|
||||
# host (both) REQUIRED. The hostname. Must not repeat
|
||||
# another entry's, and must not be a hostname
|
||||
# this release already publishes
|
||||
# (ingress.domain, ingress.admin.domain, an
|
||||
# httpRoute hostname) - the render aborts on
|
||||
# either, because one hostname cannot serve two
|
||||
# different things at /.
|
||||
# realm (both) REQUIRED. The realm as Keycloak names it. It
|
||||
# is interpolated into /realms/<realm>/account/.
|
||||
# tlsSecretName (Ingress) REQUIRED in Ingress mode, see below.
|
||||
# clusterIssuer (Ingress) OPTIONAL, see below. Empty or absent = no
|
||||
# cert-manager annotation on this object.
|
||||
# annotations (Ingress) OPTIONAL, per entry. Merged last.
|
||||
# className (Ingress) OPTIONAL. Falls back to ingress.className.
|
||||
# parentRefs (Gateway) OPTIONAL. Falls back to httpRoute.parentRefs.
|
||||
#
|
||||
# realmHosts:
|
||||
# - host: realm-one.id.example.com
|
||||
# realm: realm-one
|
||||
# tlsSecretName: tls-id-wildcard-ingress # a certificate that exists
|
||||
# clusterIssuer: "" # issued elsewhere, see below
|
||||
# annotations:
|
||||
# nginx.ingress.kubernetes.io/proxy-buffer-size: 16k
|
||||
# nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
|
||||
# - host: id.other.example.com
|
||||
# realm: realm-two
|
||||
# tlsSecretName: tls-id-other-ingress
|
||||
# clusterIssuer: letsencrypt-issuer # issued for THIS object
|
||||
#
|
||||
# (Neutral examples on purpose. Realm names, hostnames and secret names are
|
||||
# operator data and are not in this chart - they belong in the values file of
|
||||
# the installation.)
|
||||
#
|
||||
# WHY THIS DEVIATES FROM THE REPO'S INGRESS CONVENTION, which is one TLS
|
||||
# secret named tls-<release>-<chart>-ingress and the cert-manager annotation
|
||||
# ALWAYS set (style guide §4). Neither holds for these objects, and both
|
||||
# deviations are deliberate:
|
||||
#
|
||||
# - tlsSecretName is REQUIRED AND NEVER DERIVED. These hostnames live in
|
||||
# zones whose certificates already exist, and several entries typically
|
||||
# share one - a wildcard for a zone covers every realm host under it. A
|
||||
# chart-derived name per entry would ask cert-manager for a fresh
|
||||
# certificate for a name a wildcard already covers: one ACME order per
|
||||
# hostname, for nothing. So the name is operator data, and an entry
|
||||
# without one aborts the render in Ingress mode rather than rendering a
|
||||
# TLS block that silently serves the controller's default certificate.
|
||||
# - clusterIssuer is OPTIONAL, and leaving it empty is the normal case.
|
||||
# cert-manager keys a Certificate by the secret it writes, so two objects
|
||||
# naming one secret and both carrying the annotation request the same
|
||||
# certificate twice. Exactly one object per secret may carry it - the same
|
||||
# deliberate omission the qBittorrent object in freesailarr makes, for the
|
||||
# same reason. Set it only where the certificate is to be issued FOR this
|
||||
# entry's own secret.
|
||||
#
|
||||
# ANNOTATIONS ARE PER ENTRY and not chart-global, because the set is not
|
||||
# uniform: of the four hostnames the installation this was written for runs
|
||||
# today, three carry the proxy-buffer pair and one does not. One global block
|
||||
# could not express that difference. In GATEWAY MODE this field is inert, like
|
||||
# ingress.annotations - and for these hostnames that loss bites harder than
|
||||
# for the chart's own two, because an account console session is exactly the
|
||||
# large-header case: see the httpRoute block below and NOTES.txt.
|
||||
#
|
||||
# TAKING OVER HOSTNAMES THAT ANOTHER RELEASE SERVES TODAY - THE ORDER MATTERS,
|
||||
# AND IT IS NOT REVERSIBLE WITHIN A DEPLOY. These objects may already exist in
|
||||
# the cluster, created by a different repository and owned by a different Helm
|
||||
# release. Filling this list then does not edit them, it adds a SECOND object
|
||||
# for the same hostname and path, and the two orders are not equally bad:
|
||||
#
|
||||
# 1. Remove the objects in the owning repository FIRST and let that removal
|
||||
# reach the cluster.
|
||||
# 2. THEN install this chart with the list filled.
|
||||
#
|
||||
# The window between the two is a short unreachability of those hostnames.
|
||||
# The other order, or both at once, is worse: ingress-nginx's admission
|
||||
# webhook rejects the second claim on a host-and-path pair, and under Fleet
|
||||
# that leaves the bundle in ErrApplied until forceSyncGeneration is raised -
|
||||
# measured, and written down in ytdlp-web-player's collision fail. In Gateway
|
||||
# mode the webhook never fires (it compares Ingresses with Ingresses), and the
|
||||
# duplicate is accepted instead: two merged routes on one hostname, with
|
||||
# object age deciding which redirect a client gets.
|
||||
#
|
||||
# AND ONE QUESTION THIS CHART CANNOT ANSWER, UNCHECKED AS OF THIS WRITING:
|
||||
# whether anything in the owning repository depends on those objects BY NAME -
|
||||
# a Fleet bundle, a monitor, an objectset reference. Clear that before step 1,
|
||||
# not after.
|
||||
realmHosts: []
|
||||
|
||||
ingress:
|
||||
# The PUBLIC ingress - the login endpoint, the token endpoints, the account
|
||||
# console. On by default, because an identity provider nothing can reach is
|
||||
|
||||
Reference in new issue
Block a user