Give the resolver the mesh's suffix as a local domain and a module its machine's address #52

Merged
jschoubben merged 1 commits from convert/dnsmasq-from-hal into main 2026-09-23 23:13:03 +00:00
Owner

hal dnsmasq-app conversion, hq 08-connectivity. Pairs with mesh-catalog convert/dnsmasq-from-hal (PR #50) — merge this one first: the catalogue's dnsmasq module says ${machine:address}, which only this controller knows.

What changes

  • node-zones fact (internal/catalogue/facts.go): carries local=/<suffix>/ beside the address=/<node>.<suffix>/<address> lines, so a resolver that forwards never sends a mesh name it does not know upstream. The suffix default lives in one helper.
  • ${machine:address} (machine_into_files.go): a third machine fact — what at resolves to, from Rendering.Names, the same map the hosts file and wildcards are written from. Absent and refused off the network, like at. Needed because the runtime's dns list takes addresses and ${machine:at} is a name.
  • overlay.Resolver / overlay.ResolverData removed: nothing provided or consumed them; the facts: mechanism plus mesh-addressing is how a resolver gets its data.

How it is checked

  • internal/catalogue/resolver_manifests_test.go (catalogue-backed): the three manifests parse; dnsmasq's config has fixed upstreams, no-resolv, 127.0.0.1, mesh0; dnsmasq + resolv-conf compose on one machine with the machines file, the daemon restarting on it, the runtime's dns written into daemon.json at the machine's address and no runtime restart/reload ordered; resolv-conf + resolved-split-dns refused on one node (the-resolver-configuration); a machine off the network is refused rather than given a placeholder.
  • cmd/mesh-controller/network_test.go: on a real mesh (aMesh), the resolver's machines file holds a wildcard per machine on the network and local=/internal/, and is composed again without a machine that left — mirroring TestOnlyAMachineOnThePrivateNetworkIsNamed.
  • machine_into_files_test.go: ${machine:address} resolves from Names and is refused off the network.
  • go build ./... && go vet ./... && go test -count=1 ./... green with MESH_TEST_POSTGRES. gofmt -l . reports only cmd/mesh-builder/stdout_test.go, which predates this branch.

Not settled

  • The runtime's dns takes effect for containers created after the runtime next starts; the module orders no restart (a restart stops every container, ADR 0102) and dns is not among dockerd's reloadable options. On the machines being replaced the predecessor already wrote the same value. A lab check that a fresh converged node's containers resolve mesh names is not written.
  • On a converged node the filter admits port 53 from: mesh only. Containers on user-defined networks query through the runtime's embedded resolver from the host's own namespace (loopback, admitted); a container on the runtime's default bridge queries from its bridge address and is dropped. Not the case for the mesh's declared containers; flagged, not fixed.
  • The predecessor's per-node DNSMASQ_SPLIT_DNS and DNSMASQ_LOCAL_DOMAIN entries have no nox equivalent (dnsmasq's config is not a mergeable JSON file).
  • resolved-split-dns still hard-codes Domains=~internal; there is no ${machine:…} for the suffix.
  • hq 08-connectivity's "it needs no upstream" describes the split-DNS arrangement; with resolv-conf naming the resolver alone (the predecessor's arrangement) it forwards, and the design text should say so.
hal dnsmasq-app conversion, hq 08-connectivity. Pairs with mesh-catalog `convert/dnsmasq-from-hal` (PR #50) — merge this one first: the catalogue's dnsmasq module says `${machine:address}`, which only this controller knows. **What changes** - `node-zones` fact (`internal/catalogue/facts.go`): carries `local=/<suffix>/` beside the `address=/<node>.<suffix>/<address>` lines, so a resolver that forwards never sends a mesh name it does not know upstream. The suffix default lives in one helper. - `${machine:address}` (`machine_into_files.go`): a third machine fact — what `at` resolves to, from `Rendering.Names`, the same map the hosts file and wildcards are written from. Absent and refused off the network, like `at`. Needed because the runtime's `dns` list takes addresses and `${machine:at}` is a name. - `overlay.Resolver` / `overlay.ResolverData` removed: nothing provided or consumed them; the `facts:` mechanism plus `mesh-addressing` is how a resolver gets its data. **How it is checked** - `internal/catalogue/resolver_manifests_test.go` (catalogue-backed): the three manifests parse; dnsmasq's config has fixed upstreams, `no-resolv`, `127.0.0.1`, `mesh0`; dnsmasq + resolv-conf compose on one machine with the machines file, the daemon restarting on it, the runtime's `dns` written into daemon.json at the machine's address and no runtime restart/reload ordered; resolv-conf + resolved-split-dns refused on one node (`the-resolver-configuration`); a machine off the network is refused rather than given a placeholder. - `cmd/mesh-controller/network_test.go`: on a real mesh (`aMesh`), the resolver's machines file holds a wildcard per machine on the network and `local=/internal/`, and is composed again without a machine that left — mirroring `TestOnlyAMachineOnThePrivateNetworkIsNamed`. - `machine_into_files_test.go`: `${machine:address}` resolves from Names and is refused off the network. - `go build ./... && go vet ./... && go test -count=1 ./...` green with `MESH_TEST_POSTGRES`. `gofmt -l .` reports only `cmd/mesh-builder/stdout_test.go`, which predates this branch. **Not settled** - The runtime's `dns` takes effect for containers created after the runtime next starts; the module orders no restart (a restart stops every container, ADR 0102) and `dns` is not among dockerd's reloadable options. On the machines being replaced the predecessor already wrote the same value. A lab check that a fresh converged node's containers resolve mesh names is not written. - On a converged node the filter admits port 53 `from: mesh` only. Containers on user-defined networks query through the runtime's embedded resolver from the host's own namespace (loopback, admitted); a container on the runtime's *default* bridge queries from its bridge address and is dropped. Not the case for the mesh's declared containers; flagged, not fixed. - The predecessor's per-node `DNSMASQ_SPLIT_DNS` and `DNSMASQ_LOCAL_DOMAIN` entries have no nox equivalent (dnsmasq's config is not a mergeable JSON file). - `resolved-split-dns` still hard-codes `Domains=~internal`; there is no `${machine:…}` for the suffix. - hq 08-connectivity's "it needs no upstream" describes the split-DNS arrangement; with resolv-conf naming the resolver alone (the predecessor's arrangement) it forwards, and the design text should say so.
jschoubben added 1 commit 2026-09-23 23:12:56 +00:00
hal dnsmasq-app conversion, hq 08-connectivity. Converting the resolver from the module it
replaces made it forward what it cannot answer, which is what the predecessor's does, and
that found two things the controller did not say.

A resolver that forwards must not send a mesh name it does not know upstream: the
`node-zones` fact now carries `local=/<suffix>/` beside the wildcards, written here rather
than in the daemon's configuration because the suffix is the mesh's choice and this file is
the one place the mesh writes what it chose. The default lives in one helper now instead of
being spelled in two functions.

The predecessor points the container runtime's `dns` at the machine's own tunnel address —
a container cannot reach the machine's loopback. A module writing that key needs the
address, and `${machine:at}` is the machine's name; a runtime's resolver list cannot be a
name it would need that resolver to look up. So a module may say `${machine:address}`: what
`at` resolves to, read from the same names the hosts file and the wildcards are written
from, absent — and refused — off the network like `at` is.

The `mesh-resolver` and `resolver-data` constants go: nothing provided or consumed either,
the fact and `mesh-addressing` are the mechanism, and a requirement nothing provides is
refused at resolution.

Tests: the catalogue's dnsmasq, resolv-conf and resolved-split-dns manifests are parsed
and composed as a machine would receive them — fixed upstreams, no-resolv, 127.0.0.1, the
machines file, the runtime's key, the pair that decides what a machine asks refused on one
node; and on a real mesh the resolver's machines file is composed with a wildcard per
machine on the network and composed again without one that left, mirroring the hosts fact.
jschoubben force-pushed convert/dnsmasq-from-hal from dd920ff854 to 0d8264ff55 2026-09-23 23:12:56 +00:00 Compare
jschoubben merged commit 6bf42025e1 into main 2026-09-23 23:13:03 +00:00
jschoubben deleted branch convert/dnsmasq-from-hal 2026-09-23 23:13:03 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#52