Group 3 of the #114 sweep. One route, one rule, at the root - and THREE EXPECTATIONS ABOUT THIS CHART DID NOT SURVIVE CONTACT WITH IT: - #114 says bulwark has TWO Ingress objects. It has ONE. keycloak has two. - #114's measured table lists bulwark under rewrite-target. It has no rewrite-target annotation and CANNOT have one: NEXT_PUBLIC_BASE_PATH is compiled into the bundle, so the application cannot be served under a sub-path, and stripping a prefix at the proxy would yield a page that loads and then fetches everything from the wrong place. Every occurrence of the string "rewrite-target" in this chart - values.yaml, the schema, the Ingress template - is an explanation of exactly that. A grep for the string finds the refutations and counts them as occurrences. - ci/run-httproute-match-test.sh said in its header that bulwark was the second rewrite-target chart and would meet the #118 prefix/redirect assurance when group 3 landed. Group 3 landed and it does not: with no URLRewrite on a prefix other than "/", the assurance's precondition is never met here. The header is corrected rather than left standing, and it now says freesailarr is the only chart the pairing applies to. So no redirect rule is added, and none is needed. WHAT THIS MODE DROPS, MEASURED ON THE LIVE OBJECT rather than inferred. On 2026-10-12 the live Ingress on webmail.bekemeyer-klein.de carried nginx.ingress.kubernetes.io/proxy-body-size: 25m supplied by the OPERATOR through ingress.annotations - values.yaml recommends exactly that value and explains why the chart cannot pick it, so the chart sets nothing and a reader of this repository alone would not find it. It is the single annotation this installation depends on, and the Gateway API has no body-size counterpart: implementation policy, not routing. Per decisions 1 and 2 of #114 it is NAMED, not rebuilt - in values.yaml, in the schema and in the Gateway branch of NOTES.txt, in this application's own terms: an attachment above the Gateway's default is rejected before Bulwark sees a byte, the 413 comes from the proxy, and it arrives after the user has written the mail and attached the file. CORRECTED CLAIM, the same one garage and calibre-web-automated needed: the Ingress template, the bulwark.publicUrl helper and the schema all stated that nothing outside the Ingress template FUNCTIONALLY reads ingress.domain. The HTTPRoute reads it, so the claim held only while both modes were off. All three now say so, and the route carries a required() of its own. bulwark.publicUrl follows the ACTIVE mode instead of always printing ingress.domain. The two differ exactly when httpRoute.hostnames is set, which is how a migration off the Ingress runs - and printing the old host there would hand the operator an OIDC redirect URI for the hostname being migrated away from, a failure that surfaces as a rejected login at the identity provider and says nothing about this chart. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/bulwark/test-values.yaml. Part of #114
116 lines
6.5 KiB
YAML
116 lines
6.5 KiB
YAML
{{/*
|
|
HTTPRoute - the Gateway-API alternative to bulwark.ingress.yaml (Gitea #114).
|
|
|
|
ONE ROUTE, ONE RULE, AT THE ROOT - and the reason is the same one that makes
|
|
the Ingress rule permanently "/": NEXT_PUBLIC_BASE_PATH is compiled into
|
|
Bulwark's bundle, so the application cannot be served under a sub-path at all.
|
|
Which is also why THIS CHART RENDERS NO URLRewrite FILTER and needs none. That
|
|
differs from what #114's measured table and the header of
|
|
ci/run-httproute-match-test.sh both expected of this chart, so it is worth
|
|
writing down where it will be read:
|
|
|
|
- This chart has ONE Ingress object, not two (keycloak has two).
|
|
- This chart has NO rewrite-target annotation and CANNOT have one. Every
|
|
occurrence of the string "rewrite-target" in this chart - values.yaml, the
|
|
schema, the Ingress template - is an explanation of why it does not help:
|
|
the bundle's own asset and API URLs are built for the root, so stripping a
|
|
prefix at the proxy yields a page that loads and then fetches everything
|
|
from the wrong place. A `git grep rewrite-target bulwark/` finds those
|
|
refutations and counts them as occurrences, which is how the expectation
|
|
arose.
|
|
- Consequently the #118 prefix/redirect assurance in
|
|
ci/run-httproute-match-test.sh does not apply here: with no URLRewrite on a
|
|
prefix other than "/", its precondition is never met. The bare-prefix
|
|
redirect that freesailarr needs has nothing to redirect here.
|
|
|
|
WHAT THIS RENDERS, AND WHAT IT MUST NOT
|
|
|
|
Only an HTTPRoute with parentRefs. No Gateway, no gatewayClassName, no
|
|
Certificate, no implementation-specific policy object - the subset that looks
|
|
identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio (decision 1
|
|
of #114). Class and certificate belong to the PLATFORM, on the Gateway and its
|
|
listeners, in cluster-config.
|
|
|
|
WHAT THIS MODE DROPS FOR THIS CHART, MEASURED ON THE LIVE OBJECT
|
|
|
|
On 2026-10-12 the live Ingress (jk-cluster, namespace bulwark,
|
|
webmail.bekemeyer-klein.de) carried
|
|
|
|
nginx.ingress.kubernetes.io/proxy-body-size: 25m
|
|
|
|
The CHART does not set it - values.yaml recommends exactly that value and
|
|
explains why the chart cannot pick it (the right number is the mail server's,
|
|
which this chart does not know), so the 25m is the operator's, supplied through
|
|
ingress.annotations. It is therefore invisible to anyone reading this
|
|
repository alone, and it is the single annotation this installation depends on.
|
|
|
|
In this mode it is gone. The Gateway API has no body-size counterpart: it is
|
|
implementation policy, not routing, and decision 2 of #114 says such a limit is
|
|
DOCUMENTED rather than rebuilt. So the Gateway branch of NOTES.txt names it in
|
|
this chart's own terms - an attachment above the Gateway's default is rejected
|
|
before Bulwark sees a byte, with a 413 from the proxy that reads like an
|
|
application fault, on the one application whose whole purpose is sending files
|
|
to other people.
|
|
*/}}
|
|
{{- /*
|
|
Null-safe like every other block a template here dereferences (#99): an
|
|
override written without children - "httpRoute:" with its keys deleted - is
|
|
YAML null, and .Values.httpRoute.enabled would abort with a nil pointer.
|
|
*/ -}}
|
|
{{- $httpRoute := .Values.httpRoute | default dict -}}
|
|
{{- $settings := fromYaml (include "bulwark.settings" .) -}}
|
|
{{- if and $httpRoute.enabled .Values.ingress.enabled -}}
|
|
{{- fail "ingress.enabled and httpRoute.enabled are both true - set exactly one of them. The Ingress and the HTTPRoute would publish the same Service under the same hostname over two different data paths, which is already a conflict at DNS level: one name, two addresses, and which one a client reaches is luck. For Bulwark it also breaks the login: the OIDC redirect URI registered with the identity provider names ONE origin, and a session that starts on one data path and returns on the other fails on the state cookie. A migration runs over a SECOND hostname (set httpRoute.hostnames explicitly while the Ingress keeps ingress.domain) with that second origin registered as a redirect URI too, never over parallel operation of both." -}}
|
|
{{- end -}}
|
|
{{- if $httpRoute.enabled -}}
|
|
{{- $parentRefs := $httpRoute.parentRefs | default list -}}
|
|
{{- if not $parentRefs -}}
|
|
{{- fail "httpRoute.enabled is true but httpRoute.parentRefs is empty - an HTTPRoute without a parent attaches to no Gateway. It is created, it is schema-valid, and it routes NOTHING, with no event and no condition that says so; the only symptom is a hostname that does not answer. Name the cluster's Gateway, e.g. httpRoute.parentRefs: [{name: public, namespace: gateway}]." -}}
|
|
{{- end -}}
|
|
{{- /*
|
|
One source of truth for the hostname: an empty httpRoute.hostnames means
|
|
[ingress.domain]. Two values that have to agree drift apart.
|
|
|
|
AND THIS READ CHANGES THE CHART'S OWN ACCOUNT OF ITSELF. Before this mode
|
|
existed, bulwark.ingress.yaml and the bulwark.publicUrl helper both stated that
|
|
nothing outside the Ingress template FUNCTIONALLY reads ingress.domain - true
|
|
then, false now, because this template reads it. Both have been corrected
|
|
rather than left standing (the same correction garage and calibre-web-automated
|
|
needed in groups 1 and 2). The required() below is what makes it true again:
|
|
ingress.domain is mandatory in Gateway mode too, unless hostnames are given
|
|
explicitly.
|
|
*/ -}}
|
|
{{- $hostnames := $httpRoute.hostnames | default list -}}
|
|
{{- if not $hostnames -}}
|
|
{{- $hostnames = list (required "A Domain/Host must be provided: set ingress.domain (used as the HTTPRoute's single hostname when httpRoute.hostnames is empty) or set httpRoute.hostnames explicitly" .Values.ingress.domain) -}}
|
|
{{- end -}}
|
|
apiVersion: gateway.networking.k8s.io/v1
|
|
kind: HTTPRoute
|
|
metadata:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-httproute
|
|
labels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
spec:
|
|
parentRefs:
|
|
{{- toYaml $parentRefs | nindent 4 }}
|
|
hostnames:
|
|
{{- toYaml $hostnames | nindent 4 }}
|
|
rules:
|
|
{{- /*
|
|
The Ingress counterpart is `path: / , pathType: Prefix`, and "/" here is
|
|
as permanent as it is there - see the header. The port is the Service's
|
|
port NUMBER and not its name "http": backendRefs takes a PortNumber, so
|
|
unlike the Ingress this reference cannot be made by name. It comes from
|
|
the same settings helper the Service renders its port from, so
|
|
bulwark.port and this file still cannot drift apart;
|
|
ci/run-httproute-match-test.sh holds the pair for the case where they do.
|
|
*/}}
|
|
- matches:
|
|
- path:
|
|
type: PathPrefix
|
|
value: /
|
|
backendRefs:
|
|
- name: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
|
port: {{ $settings.port | int64 }}
|
|
{{- end }}
|