Traefik Gateway Header Injection
CVE-2026-54765 is a filter-merging flaw in Traefik's Kubernetes Gateway API provider allowing cross-tenant header injection via shared backends.
Overview
CVE-2026-54765 is a header injection vulnerability rooted in how Traefik’s Kubernetes Gateway API provider reconciles HTTPRoute objects that share a backend Service:port combination but carry distinct backendRef filters. When two accepted routes converge on the same backend, Traefik collapses them into a single child service configuration and applies only one route’s filter set — non-deterministically — to all traffic reaching that backend. The result is that request-scoped headers set by filters (tenant identity, authorization context, internal trust headers) bleed across route boundaries, corrupting the security context of unrelated requests.
The vulnerability was introduced in v3.7.0 alongside expanded Gateway API support and affects all releases up to and including v3.7.5. Clusters running this version range that expose the Kubernetes Gateway API provider are directly at risk, with severity amplified in multi-tenant deployments where ReferenceGrant objects permit cross-namespace backend targeting — a scenario that is explicitly encouraged by the Gateway API specification for shared infrastructure.
The practical blast radius extends to any workload that trusts headers set by Traefik filters rather than performing independent authentication. Backend services that accept an X-Tenant-ID, X-User-Role, or similar header as authoritative input — a common pattern in service-mesh and API-gateway architectures — may process requests under the wrong identity without any indication that filter substitution has occurred.
Technical Analysis
The root cause lies in Traefik’s route-to-service mapping logic inside the Gateway API provider. When building the internal routing tree, the provider maps each HTTPRoute backend reference to a named child service. The naming scheme for these child services is derived from the target Service:port coordinates. Two HTTPRoute objects pointing to the same Service:port therefore resolve to the same internal service key, even when their backendRef filters differ.
The vulnerable pattern looks structurally like this (simplified from the provider reconciliation code):
// Vulnerable: child service key is derived solely from Service:port,
// ignoring the filter set attached to this specific backendRef.
func buildChildServiceKey(namespace, name string, port int) string {
return fmt.Sprintf("%s-%s-%d", namespace, name, port)
}
func (p *Provider) buildHTTPRouteConfig(route *gatewayv1.HTTPRoute) {
for _, rule := range route.Spec.Rules {
for _, backendRef := range rule.BackendRefs {
key := buildChildServiceKey(
string(*backendRef.Namespace),
string(backendRef.Name),
int(backendRef.Port),
)
// If a service with this key already exists, it is reused.
// The filters from *this* backendRef are silently discarded
// because the map entry was already populated by a prior route.
if _, exists := p.serviceMap[key]; exists {
continue // <-- filters from this route are dropped
}
p.serviceMap[key] = buildServiceConfig(backendRef.Filters)
}
}
}
The continue branch — or its logical equivalent in the actual codebase — means that whichever HTTPRoute wins the insertion race determines the filter set that all subsequent routes sharing that backend key will inherit. This is not a transient race condition in the concurrency sense; it is a deterministic but order-dependent logic error in the reconciliation loop. The “winner” is typically the route processed first during a resync, which in practice correlates with object creation order or Kubernetes API list ordering — neither of which is under the victim tenant’s control.
The backendRef filter types relevant here include RequestHeaderModifier and ResponseHeaderModifier, both of which are commonly used to stamp requests with identity or routing metadata before they reach the backend. Because Traefik applies these filters at the proxy layer before the upstream ever sees the request, the backend has no way to distinguish a legitimately filtered request from one that received the wrong tenant’s filter set.
Cross-namespace exploitation requires a ReferenceGrant that permits the attacker-controlled namespace to target the victim’s backend Service:port. Such grants are routine in shared-cluster environments where a platform team exposes common backend services to multiple product teams.
Impact
An attacker with the ability to create an accepted HTTPRoute in any namespace that shares — or is granted access to — a backend Service:port targeted by a victim route can cause their filter context to be applied to the victim’s requests. Concretely:
- Tenant impersonation: If the victim route sets
X-Tenant-ID: tenant-avia aRequestHeaderModifier, the attacker’s filter may replace it withX-Tenant-ID: tenant-b, causing the backend to process tenant-a’s requests under tenant-b’s identity. - Privilege escalation: Authorization headers (
X-User-Role: admin,X-Internal-Token: <value>) set by trusted route filters can be overwritten with attacker-controlled values. - Data exfiltration boundary crossing: In multi-tenant SaaS deployments, this translates directly to unauthorized cross-tenant data access without any credential compromise.
The CVSS 8.5 (High) rating reflects high confidentiality and integrity impact with no required authentication beyond the ability to create a Kubernetes HTTPRoute object — a permission commonly granted to application developers in self-service cluster models. Availability impact is rated low because the service continues to function; it simply does so under the wrong security context.
How to Fix It
Upgrade immediately to Traefik v3.7.6. The patch modifies the child service key derivation to incorporate a stable hash of the backendRef filter set, ensuring that two backend references targeting the same Service:port but carrying different filters produce distinct internal service entries and are not collapsed.
The corrected pattern:
// Fixed: child service key includes a deterministic hash of the filter set,
// so routes with different filters always produce distinct service entries.
func buildChildServiceKey(namespace, name string, port int, filters []gatewayv1.HTTPRouteFilter) string {
filterHash := computeFilterHash(filters)
return fmt.Sprintf("%s-%s-%d-%s", namespace, name, port, filterHash)
}
func computeFilterHash(filters []gatewayv1.HTTPRouteFilter) string {
h := fnv.New64a()
for _, f := range filters {
encoded, _ := json.Marshal(f)
h.Write(encoded)
}
return fmt.Sprintf("%x", h.Sum64())
}
Upgrade commands:
# Helm-managed deployments
helm repo update
helm upgrade traefik traefik/traefik --version 3.7.6
# Static binary / container image
docker pull traefik:v3.7.6
# Verify the running version
kubectl exec -n traefik deploy/traefik -- traefik version
Until the upgrade is applied, consider restricting ReferenceGrant objects to explicitly trusted namespace pairs and auditing all HTTPRoute objects for shared Service:port targets with differing filter sets.
Our Take
This vulnerability exemplifies a class of logic errors we consistently encounter when routing infrastructure evolves faster than its security model: deduplication logic that is correct for functional routing but incorrect when security-sensitive metadata is attached to the deduplicated objects. The same pattern appears in service mesh sidecar injection, API gateway policy merging, and CDN edge configuration — anywhere a platform layer normalizes multiple configurations into a shared internal representation.
For enterprises running multi-tenant Kubernetes clusters, the deeper lesson is that headers set by infrastructure layers should never be the sole authorization signal at the backend. Defense in depth requires backends to validate identity independently, treating infrastructure-set headers as hints rather than proofs. Traefik’s filter mechanism is a powerful convenience; it should be paired with backend-side validation that can detect or reject unexpected header values.
Detection with SAST
This vulnerability class maps to CWE-706 (Use of Incorrectly-Resolved Name or Reference) and CWE-639 (Authorization Bypass Through User-Controlled Key). From a SAST perspective, Offensive360’s analysis engine flags this pattern by:
- Deduplication-without-attribute-inclusion: Detecting map or cache insertion patterns where the lookup key is derived from a subset of the object’s fields while security-relevant fields (filter sets, policy annotations, ACL entries) are excluded from the key but influence downstream behavior.
- Dead-write detection on security attributes: Identifying code paths where a structured object’s security-bearing fields are computed and then silently discarded on a map-hit branch (
if exists { continue }). - Cross-resource identity propagation: Tracing how externally-supplied metadata (Kubernetes resource fields, HTTP headers) flows through routing configuration builders into outbound request modification — flagging cases where multiple input sources can overwrite a single output channel without reconciliation.
These rules operate at the inter-procedural level and require taint-aware data-flow analysis across the reconciliation loop, which is why simpler pattern-matching tools miss them. Any HTTPRoute-processing code that maps backend references without hashing their full attribute set should be treated as a finding worthy of manual review.
References
Detect this vulnerability class in your codebase
Offensive360 SAST scans your source code for CVE-2026-54765-class vulnerabilities and thousands of other patterns — across 60+ languages.