The laptop's VPN client replaces the mesh's resolver file on every connect and the node-engine writes it back minutes later, so either the mesh's names or the corporate ones fail for whole sessions. Measure how the client sets DNS and weigh how names can be routed by domain without breaking ADR 0223's one-answer rule.
90 lines
7.1 KiB
Markdown
90 lines
7.1 KiB
Markdown
# 03 — Recommendation
|
||
|
||
From the [evidence](01-evidence.md) and the [options](02-options.md): **route by domain on the one
|
||
machine that runs a VPN client, and leave every other machine as ADR 0223 has it.** On a machine whose
|
||
operator declares a VPN client, the uplink's holder runs systemd-resolved as the machine's resolver,
|
||
listening on the machine's private address; the mesh's link carries the two mesh resolvers for the
|
||
mesh's domain and as the default route; the moment the VPN client writes its own resolver file, the
|
||
holder hands that file's servers and domains to the tunnel link and writes the mesh's file back. The
|
||
fight ends because the two programs no longer want different things: the VPN's servers are used, for the
|
||
VPN's domains only.
|
||
|
||
Why not the cheaper ones: adding the VPN's servers to the file breaks the one-answer rule that ADR 0223
|
||
exists for (option 2); the mesh's resolvers cannot reach the VPN's servers (option 3); standing back cuts
|
||
the laptop off from the mesh for whole working days (option 4). Option 4 is kept as what the machine does
|
||
*until* this is built, if the operator prefers it to today's fight — a choice for the operator, stated in
|
||
the proposed text as an interim setting, not as the decision.
|
||
|
||
What it asks of the sibling proposal, ADR 0241 (machine network health): on a machine that declares a VPN
|
||
client, the VPN client's file is not an outside writer's fault but an expected handover — it is reported
|
||
as *adopted* once the holder has taken its servers, and as a fault only if it stands longer than the
|
||
watch's bound (a few seconds), which means the watch did not fire.
|
||
|
||
## Proposed decision text
|
||
|
||
Ready for graduation through playbook 02. The number is assigned then.
|
||
|
||
> **A machine with a VPN client routes names by domain, and the VPN's file is taken, not fought**
|
||
>
|
||
> **Context.** ADR 0223 gives every machine one resolver file listing the mesh's two resolvers and
|
||
> nothing else, written by the uplink's holder (ADR 0117). A corporate VPN client on the laptop moves that
|
||
> file aside when it connects and writes its own — two servers reached through its tunnel, eight search
|
||
> domains — and puts its backup back on disconnect; it never re-asserts its file and never gives
|
||
> systemd-resolved per-link settings, even when resolved runs. The node-engine wrote the mesh's file back
|
||
> in nine of nine sessions, within 1 s to 3.5 min; from then on no corporate name resolved for the rest of
|
||
> sessions lasting up to fourteen hours, and before it no mesh name did. No resolver file can list both
|
||
> sets of servers: musl takes the first reply and glibc the first server's "no such name", so one kind of
|
||
> name would fail (issue 262).
|
||
>
|
||
> **Decision.**
|
||
>
|
||
> 1. **A machine declares that it runs a VPN client**, as a setting of its uplink's holder, naming the
|
||
> client. Nothing changes on a machine that does not.
|
||
> 2. **On such a machine the uplink's holder runs systemd-resolved** as the machine's only resolver, with
|
||
> its stub also listening on the machine's private address, so the machine's containers reach it. The
|
||
> private network's link carries the mesh's resolvers, in ADR 0223's order, as the routing domain of the
|
||
> mesh's names and as the default route. `/etc/resolv.conf` lists the machine's private address alone,
|
||
> with ADR 0223's options. resolved holds no copy of any mesh name: every mesh name is asked of the two
|
||
> resolvers, as on every other machine.
|
||
> 3. **The VPN client's file is taken, not fought.** The holder watches the resolver file; when the
|
||
> declared client writes it, the holder gives the tunnel link the file's servers and its search domains
|
||
> as routing domains, and writes the mesh's file back, within seconds — not at the next reconcile. When
|
||
> the tunnel goes, resolved drops its link's settings; the client's restore of its backup is corrected
|
||
> by content, as any change to the file is.
|
||
> 4. **The VPN's domains are never the mesh's.** They are not written to the mesh's store, its resolvers
|
||
> or any other machine; they live in resolved's state for the life of the tunnel.
|
||
> 5. **The machine says it.** The machine's network health (ADR 0241, proposed) states the VPN's file as
|
||
> *adopted*, with the tunnel link and the count of domains routed, and raises it as an outside writer
|
||
> only if it stands longer than the watch's bound or the client is not the declared one.
|
||
> 6. **Until rules 2–3 run on a machine, its operator may choose to defer**: the node-engine leaves the
|
||
> declared client's file in place while the client's backup holds the mesh's file, and the machine's
|
||
> health says *deferred to the VPN client — mesh names do not resolve* for as long as it lasts.
|
||
>
|
||
> **Consequences.** ADR 0223's rejection of a local forwarder stands for every machine but one that
|
||
> declares a VPN client; there its three objections are answered — one machine, not every machine;
|
||
> reachable from containers on the private address; no copy of the mesh's names. A machine that
|
||
> declares a VPN client and whose resolved is down has no names; its network health says so. The
|
||
> routes the VPN client adds are untouched: its routes include `10.0.0.0/8`, and the
|
||
> mesh's private network keeps working only because its own route is more specific — a fact to keep in
|
||
> mind when choosing a private range, and a check of its own.
|
||
>
|
||
> **How it is checked.**
|
||
>
|
||
> | Rule | Checked by |
|
||
> |---|---|
|
||
> | 1 Declared, nothing changes elsewhere | a controller composition test: a machine without the setting composes exactly as before; with it, the holder declares resolved and the watch |
|
||
> | 2 resolved, on the private address, mesh link and default route | a composition test reading the holder's files; live on the laptop: `resolvectl status` shows the mesh link with `~internal` and `~.`, the stub on the private address, and the machine's file lists that address alone |
|
||
> | 3 Taken within seconds | a lab drill: a container plays the VPN client (writes a file with its own header, servers and search lines, adds a tunnel link); the tunnel link carries the servers and domains and the mesh's file is back within 5 s; removing the link drops them |
|
||
> | 3 Containers | the lab drill: an Alpine container on a bridge resolves a mesh name ten times out of ten while the fake tunnel is up |
|
||
> | 4 Never the mesh's | a test that the store, the controller's renders and the other machines' files carry none of the VPN's domains after the drill |
|
||
> | 5 Said | the machine network health test: the VPN's file read as *adopted*, not raised; raised when it stands past the bound |
|
||
> | 6 Defer, while not built | a node-engine test: with the setting and the client's backup present, the file is not corrected and the health says deferred; without the backup it is corrected |
|
||
> | the routes | the machine network health's check that a mesh address still routes over the private link (ADR 0241, proposed) |
|
||
|
||
## What this does not decide
|
||
|
||
- Which VPN client the operator uses (option 6): the employer's.
|
||
- Resolution for a machine with *two* VPN clients — none exists.
|
||
- Whether the operator's corporate names should resolve inside the mesh's containers on the laptop: they
|
||
will, through the same router; restricting them is a later question if it matters.
|