ADR 0167: a membership carries what its module receives, and who the mesh is
Issue 191's route proxy needs to know who the mesh is to serve an internal name correctly, and the first fix had it work that out alone. The membership on the bus now carries it, from the same list the filter uses. ADR 0138 gains an insight that the proxy is where internal reach is kept; designs 08 and 25 say how.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
---
|
||||
status: located
|
||||
opened: 2026-10-01
|
||||
located-in: [mesh-controller examples/route-proxy/main.go (routesFrom requires a route's public `name` and treats `internal-name` only as an alias of it)]
|
||||
located-in: [mesh-controller examples/route-proxy/main.go (routesFrom requires a route's public `name` and treats `internal-name` only as an alias of it; the handler serves every routed name to any source), mesh-controller internal/broker/membership.go (a membership says nothing of what its module receives or who the mesh is)]
|
||||
fixed-by:
|
||||
amended-design: []
|
||||
amended-design: [03-DESIGN/01-to-be/08-connectivity.md, 03-DESIGN/01-to-be/25-the-bus-on-nats.md]
|
||||
---
|
||||
|
||||
# 191 — A route with only an internal name is dropped as naming nothing
|
||||
|
||||
@@ -23,9 +23,53 @@ mesh before ADR 0138, when both names were always composed. It has been wrong si
|
||||
The other half of the proxy already handles the case. Certificates for a host are split by whether it
|
||||
is in the public set: hosts outside it go to the internal authority, and only hosts inside it are
|
||||
eligible for ACME. A host that is only ever an internal name falls on the correct side of both checks
|
||||
without change. Only reading the route was wrong.
|
||||
without change. For certificates, only reading the route was wrong; who may reach the route is the next section.
|
||||
|
||||
**The fix.** `routesFrom` takes a route that names either host, serves each name it carries, and
|
||||
marks only the public one as public. It still skips a route that names neither, with the same log line.
|
||||
A test proves an internal-only route is served, certified by the internal authority, and refused by
|
||||
the public one. That test fails against the code before the change.
|
||||
|
||||
## The first fix would have made the name public — 2026-10-02, from review
|
||||
|
||||
Serving the dropped route was not enough. The proxy picks a route from the name a request carries
|
||||
and never from where the request came from, and it answers public and internal names on the same
|
||||
listeners. Its public names resolve to an address the internet reaches. So once the internal-only
|
||||
route was served, any request from the internet carrying `unifi.home-server.internal` — a name of a
|
||||
fixed, guessable shape — would have reached an administration interface that reach `internal` was
|
||||
chosen to keep private. Before the fix the route was unreachable from everywhere. After it, it would
|
||||
have been reachable from everywhere. Two more leaks came with it: the proxy's answer for an unrouted
|
||||
name listed every name it serves, internal ones included, and the handshake handed a certificate
|
||||
naming the internal host to any client.
|
||||
|
||||
Nothing showed this while every routed endpoint also had a public name: its internal name exposed
|
||||
nothing the public one did not. It is a gap in the decision's wording, not only in the proxy — ADR
|
||||
0138 says the proxy *serves* the internal name without saying to whom — so it is recorded there as a
|
||||
progressive insight and in the to-be connectivity design.
|
||||
|
||||
**Where "inside" is decided: told, not worked out.** The first correction had the proxy work it out
|
||||
for itself — the mesh's range from an environment variable the catalogue wrote, and the machine's
|
||||
container bridges from its own interfaces. That was a second definition of "the mesh", kept by one
|
||||
module beside the one the controller already has: it resolves "from the mesh" to every machine's
|
||||
address on the private network, and the packet filter is rendered from that list. Reviewed, it was
|
||||
replaced: [ADR 0167](../../02-DECISIONS/0167-a-membership-carries-what-its-module-receives-and-who-the-mesh-is.md)
|
||||
has every membership on the bus carry what its module receives and that list, and the proxy follows
|
||||
its membership. One composition, read by the filter and by the proxy.
|
||||
|
||||
The proxy reads the source address, where the guard reads the interface, because it cannot see the
|
||||
interface a request arrived on. A claimed source does not carry here: a connection needs its replies,
|
||||
and replies to a mesh address leave by the tunnel.
|
||||
|
||||
**What changed with it.** The internal name of a route that also has a public one is now served to the
|
||||
mesh only, like any other internal name. Outsiders have the public name, so nothing they could reach is
|
||||
lost. A container calling its own machine's internal name arrives from its container network and is
|
||||
refused; whether the mesh should issue those networks too is left open in ADR 0167.
|
||||
|
||||
**Order of release.**
|
||||
|
||||
1. The catalogue change, which gives the proxy a bus account. A machine running the proxy is not
|
||||
composed until its account is issued, so the account is issued straight after
|
||||
(`module issue route-proxy --node <machine>`), and then the machine is pushed.
|
||||
2. The controller and proxy change. The push after it publishes memberships that carry the routes and
|
||||
the mesh, and each proxy takes them. Until then, a proxy serves its file, and internal names to its
|
||||
own machine alone.
|
||||
|
||||
Reference in New Issue
Block a user