diff --git a/cmd/mesh-controller/mesh_own_names_test.go b/cmd/mesh-controller/mesh_own_names_test.go new file mode 100644 index 0000000..bfbb320 --- /dev/null +++ b/cmd/mesh-controller/mesh_own_names_test.go @@ -0,0 +1,27 @@ +package main + +import ( + "reflect" + "testing" +) + +// The mesh's resolver holds only the mesh's own names (novox/hq ADR 0191): a routed public name is +// never given a private answer, and a route's internal name is. +func TestOnlyTheMeshsOwnNamesAreAnsweredPrivately(t *testing.T) { + routes := map[string]string{ + "git.example.tld": "10.77.0.1", + "example.tld": "10.77.0.1", + "media.home.example": "10.77.0.2", + "git.anchor.internal": "10.77.0.1", + "media.homeserver.internal": "10.77.0.2", + "internal.example.tld": "10.77.0.1", // the suffix as a label, not as the zone + } + got := meshOwnNames(routes, "internal") + want := map[string]string{ + "git.anchor.internal": "10.77.0.1", + "media.homeserver.internal": "10.77.0.2", + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("names answered privately: %v\nwant only the mesh's own: %v", got, want) + } +} diff --git a/cmd/mesh-controller/plan.go b/cmd/mesh-controller/plan.go index ef8712c..55a7a13 100644 --- a/cmd/mesh-controller/plan.go +++ b/cmd/mesh-controller/plan.go @@ -645,10 +645,9 @@ func renderingFor(ctx context.Context, open *stores, node string, } } - // And every routed name → the node that serves it (novox/hq ADR 0066). Alongside the - // `.internal` names above, so a container — or an internal ACME validator — resolves a - // routed name to the proxy that serves it, mesh-wide. The mesh publishes the names it was told - // to serve and knows nothing about what they mean. + // And every routed name under the mesh's own suffix → the node that serves it, alongside the + // `.internal` names above (novox/hq ADR 0066, narrowed by ADR 0191). A public name is not + // among them: the mesh gives no private answer for a name public DNS answers. // Kept apart from the machines, because a fact about the machines must not be handed the names // the mesh merely serves (novox/hq 04-ISSUES/111). machines := make(map[string]string, len(names)) @@ -659,7 +658,7 @@ func renderingFor(ctx context.Context, open *stores, node string, if err != nil { return catalogue.Rendering{}, inventory.Node{}, err } - for name, at := range routes { + for name, at := range meshOwnNames(routes, overlay.Suffix()) { names[name] = at } @@ -740,6 +739,25 @@ func renderingFor(ctx context.Context, open *stores, node string, }, record, nil } +// meshOwnNames is the routed names the mesh may answer privately: those under its own suffix. +// +// **The mesh's resolver holds only the mesh's own names** (novox/hq ADR 0191). A routed public name +// was once published here at its serving node's private address, so an internal authority could +// reach it to certify it (ADR 0066). Every machine's resolver then answered public names with +// addresses only members can reach — and a resolver that also serves a LAN handed them to a phone +// on it, which could not reach the mail server while every check, run from a member, passed. A +// route is reached and certified inside the mesh by its internal name (ADR 0151); its public name +// resolves publicly, for members and everyone else alike. +func meshOwnNames(routes map[string]string, suffix string) map[string]string { + out := map[string]string{} + for name, at := range routes { + if strings.HasSuffix(name, "."+suffix) { + out[name] = at + } + } + return out +} + // routeNamesInTheMesh is every routed name and the address of the node that serves it (novox/hq // ADR 0066). //