From 226d5567432095ef60287e3c8722fea97fc2979c Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 25 Sep 2026 11:51:56 +0200 Subject: [PATCH] Issue 116: route-proxy has no authentication or IP-restriction mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comparing route-proxy against what HAL's actual Traefik config does today, not Traefik's general feature set, per the standing rule that the nox mesh must do at minimum what the HAL mesh it replaces already does. Everything else checked out even or better; these two are real, confirmed gaps — RedisInsight has no login of its own and depends entirely on Traefik's basicauth middleware, and the gitea-internal route depends on an IP-scoped deny rule. Neither has any equivalent in route-proxy's single-lookup request path. --- .../00-report.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 04-ISSUES/116-route-proxy-has-no-auth-or-ip-restriction/00-report.md diff --git a/04-ISSUES/116-route-proxy-has-no-auth-or-ip-restriction/00-report.md b/04-ISSUES/116-route-proxy-has-no-auth-or-ip-restriction/00-report.md new file mode 100644 index 0000000..a814001 --- /dev/null +++ b/04-ISSUES/116-route-proxy-has-no-auth-or-ip-restriction/00-report.md @@ -0,0 +1,74 @@ +--- +status: located +opened: 2026-09-25 +located-in: [mesh-controller examples/route-proxy] +fixed-by: +amended-design: +--- + +# 116 — `route-proxy` has no authentication or IP-restriction mechanism, and two live HAL routes depend on one + +## What was observed + +On the control-node, 2026-09-25, deciding whether `route-proxy` (novox/hq 08-connectivity §3, +`mesh-controller/examples/route-proxy/main.go`) can replace HAL's adopted Traefik instance as the +permanent public ingress. The standing requirement is that the nox mesh does, at minimum, what the +HAL mesh it is replacing already does — so the comparison is against Traefik's *current, real* +configuration on this node, not Traefik's general feature set. + +Reading `/services/traefik/dynamic/` on the control-node directly, two of its routes carry +middleware `route-proxy` has nothing to match: + +``` +redisinsight.novox.be → traefik.http.middlewares.redisinsight-auth.basicauth.users: + (RedisInsight has no login of its own; this is the only gate in front of it) +gitea-internal → zz-security-gitea-internal-deny.yml — an IP-scoped deny rule +``` + +`route-proxy`'s entire request path is `main.go`'s `handler()`: one lookup into the routing table +built from `$ROUTES`, then `proxy.ServeHTTP(w, r)` — no middleware chain, no auth check, no source +filtering, anywhere in the file. Confirmed by reading the whole of `main.go` (261 lines): the only +things it does per-request are route lookup and proxy. It was designed this way deliberately — "a +route hands back a name, not a credential" (the module's own README) — but that design covers the +*route grant*, not what a request arriving at the name is allowed to do, which is what these two +HAL routes need. + +Everything else compared cleanly or better, and is **not** part of this issue: + +- Streaming/large request bodies (why Traefik needed `minio-api-body-size: maxRequestBodyBytes: + 0` for the object store's routes): `net/http/httputil.ReverseProxy` streams with no default cap, + so this need requires nothing extra here. +- WebSocket/connection upgrades (used by at least one console route): native in + `httputil.ReverseProxy` since Go 1.12; not yet verified live against this proxy, but not absent + by design the way auth is. +- TLS/ACME: `route-proxy`'s own `HostPolicy` (`onlyWhatTheMeshSaid`) refuses to certify anything + the mesh did not actually route, and defaults to Let's Encrypt staging until a node opts in to + production (novox/hq 04-ISSUES/004) — stricter than the hand-maintained Traefik config it would + replace. +- Multiple public names for one module (minio's `files-api` / `files`): already solved by the + `contributes` many-shape (mesh-controller PR #55), needs nothing further from the proxy. + +## Why it matters + +Two routes cannot move off Traefik until this is closed, and — under the standing "at minimum" +rule — nothing wholesale can be called a Traefik replacement while it is. Left unfixed, either: + +- those two routes stay on `route-adapter`/Traefik indefinitely, which means the mesh is running + two reverse proxies side by side for no principled reason, or +- somebody migrates them anyway and RedisInsight (no auth of its own) or the gitea-internal path + (currently IP-restricted) becomes reachable to anyone who can resolve the name — a real exposure, + not a cosmetic gap. + +## Open questions + +- Where does the auth/restriction config come from? The contract today is "one contribution, a + name and a port" (`main.go`'s `contribution.Values`) — extending it to optionally carry an + htpasswd hash or an allowed-CIDR list keeps the mesh (not the proxy) as the source of truth, + consistent with everything else `route` already does. An alternative — a second, proxy-specific + settings layer, keyed by route name — decouples it from the route grant itself but adds a second + place to look. Worth deciding deliberately rather than defaulting to whichever is less code. +- Basic auth and an IP allow/deny list cover what HAL's config uses today. Whether that is the + right *general* shape for route-level middleware, or just the two cases observed so far, is + itself worth a decision before writing it — the module's own README already states "the contract + is the file, not this program," so nothing here requires all of it to live in this one reference + implementation.