Issue 116: route-proxy has no authentication or IP-restriction mechanism #109
@@ -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: <htpasswd hash>
|
||||
(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.
|
||||
Reference in New Issue
Block a user