deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Traefik 3.6 child router isolation survives 500 spoofed-header requests in independent test

An independent dev.to test confirms Traefik 3.6's claim that child routers cannot be called directly: 500 spoofed-header requests all fell through to a catchall. A version-mixing pitfall also surfaced.

Traefik 3.6 child router isolation survives 500 spoofed-header requests in independent test

What was tested

Traefik 3.6, released in September 2026, introduced multi-layer routing: a parent router runs middleware, and child routers declared with the new parentRefs field are only evaluated after the parent's rule and middleware chain have already run. Traefik's announcement claims child routers cannot be called directly, which is a load-bearing assertion for anyone using the feature to gate services behind an upstream auth step that sets a trusted header.

According to a write-up on dev.to, one engineer put that claim under attack rather than taking the release notes at face value. The test rig ran Traefik 3.6.25 in Docker with the file provider and three whoami containers standing in for backends. A parent router matched a PathPrefix rule on /api and ran a middleware that unconditionally set X-Role: admin; a child router matched that header via a regex and parentRefs, and pointed at an admin service; a low-priority catchall served everything else. In a real deployment, the author noted, the header-setting middleware would be a forward-auth check based on an actual login rather than a static assignment.

The bypass attempt

Baseline behavior matched the documentation: requests through /api carried X-Role: admin regardless of what the client sent, because the parent's middleware overwrote the header before the child router ever evaluated it.

The attack was to send the header the child matches on, but on a path that never touches the parent. If the child rule were evaluated independently, its regex would match a bare request carrying a spoofed X-Role: admin at the root. It did not: the request landed on the catchall service, as though the child router did not exist for it.

A single request proves little about a router table under contention, so the author fired 500 requests from 50 concurrent workers, each spoofing X-Role: admin on paths outside /api. Zero reached the admin service; all 500 were served by the catchall. The author called this the one reassuring finding of the exercise: the release claim held under an actual attempt to break it, not just under a docs example.

Configuration pitfalls

Getting to a working setup surfaced three distinct failure modes:

  • Child routers cannot declare their own entryPoints. Doing so disables the router with the error "non-root router cannot have Entrypoints configuration"; a child inherits its entry point from the parent.
  • The matcher is HeaderRegexp, singular. A plural variant, which the author had taken from a summary of Traefik's own blog post, fails with "unsupported function" and reads like a syntax error rather than a wrong function name.
  • A typo in parentRefs disables more than the broken child. The parent router itself also errors out, because a parent with no service and no resolvable children has nothing to do. In the author's case, a bad parent reference silently stopped the entire /api prefix from routing until it was fixed.

Three-level nesting also worked: a grandparent matching /api, a parent matching the role header the grandparent set, and a child matching a second header set by the parent's own middleware routed correctly only when all three conditions were satisfied in order.

The version-mixing risk

The sharper operational finding came from backward-compatibility testing. Pointing the same dynamic configuration at Traefik 3.5.6, the previous minor version, did not simply ignore the new field. The older version rejected the entire file with "field not found, node: parentRefs". Every router defined in that file stopped being served, including the plain catchall that had nothing to do with multi-layer routing, leaving requests with 404 responses.

The consequence for operators: if a fleet of Traefik instances shares dynamic configuration and 3.6 is rolled out gradually, any instance still on 3.5 that reads a file containing parentRefs loses the routing that file defines.

Why it matters

Header-based routing is only as safe as the guarantee that clients cannot forge the headers your child rules trust. This test provides independent, adversarial evidence that Traefik 3.6's parent-child boundary enforces that guarantee: child routers appear invisible to requests that never traversed the parent. For teams building auth-gated service groups on the feature, that is the core assumption, and it survived a direct attempt to break it, though it remains one configuration on one version rather than a formal audit.

The version-mixing behavior is the practical takeaway for mixed fleets: an unknown field in a dynamic config file takes down all routing from that file on older versions, so configuration changes and binary upgrades have to be sequenced together.

  • #traefik
  • #reverse-proxy
  • #routing
  • #security
  • #docker

Related posts