Issue 339: places and accesses only at the terminal, never at the machine's own trees; a setting is one line #168

Merged
mesh-admin merged 1 commits from fix/339-places-and-accesses-are-the-terminals into main 2026-10-08 22:21:02 +00:00
Contributor

Critical security fix (hq issue 339). Do not merge without review. Independent of mesh-host's PR for the same issue; either may merge first.

The hole: any caller of mesh-controller.settings (or mesh-controller.command "settings set …") could set a module's places to /etc with an owner of its own, and the node-engine would chown it as root at the next push; or set accesses to / and have it mounted into a container.

  • Terminal only. settingsCommand refuses any layer change that adds, changes or removes places or accesses, and a clear of a layer holding either, in every process a verb runs (runVerb sets MESH_VERB for every verb; the terminal has none). A change of other keys that keeps both as they were is still taken.
  • The generic command verb refuses settings set / settings clear lines outright (argvFor), pointing at the settings verb.
  • Never at the machine's own trees, from anywhere (the terminal too): Places and AccessPlaces clean the path and refuse at/below /etc, /usr, /boot, /root, /run, /proc, /sys, /dev, /bin, /sbin, /lib*, /var/lib/mesh, /var/lib/mesh-host, and exactly /, /var, /var/lib, /home, /srv, /mnt, /opt, /tmp, … .
  • A setting is one line: JudgeSettings (where a layer is kept and where it is composed) refuses \n, \r, NUL in any string or key at any depth. PEM blocks alone may hold lines (strict: base64 lines of 64, last padded), because step-ca serves its root to the route proxy as a setting. The co-located test's fake PEM fixture was 46 characters on its last line (not a whole number of base64 groups) and is now 48.

Tests that fail on main (run there first):

  • cmd/mesh-controller/terminal_settings_test.go TestPlacesAndAccessesAreRefusedThroughEveryVerb: through argvFor + this binary's run() dispatch with MESH_VERB set as runVerb does — settings verb (one machine, whole mesh, move, drop with replace, clear) and command lines (three spellings, plus clear) are refused and the layer is unchanged; the terminal takes the placement; system paths refused at the terminal; line breaks refused. On main: settings verb, one machine: <nil>.
  • internal/catalogue/terminal_settings_test.go: system paths for places and accesses, one-line rule, PEM shape. On main: every system path taken, every line break taken.

Live check before writing it: the 15 layers that set places/accesses today (all on the home server and the anchor's backup) pass the new refusal; details in the issue.

merge-check.sh passes locally against a throwaway postgres (no C compiler is missing here: race-checked).

hq: issue 339 (PR to follow).

**Critical security fix (hq issue 339).** Do not merge without review. Independent of mesh-host's PR for the same issue; either may merge first. The hole: any caller of `mesh-controller.settings` (or `mesh-controller.command "settings set …"`) could set a module's `places` to `/etc` with an owner of its own, and the node-engine would chown it as root at the next push; or set `accesses` to `/` and have it mounted into a container. - **Terminal only.** `settingsCommand` refuses any layer change that adds, changes or removes `places` or `accesses`, and a clear of a layer holding either, in every process a verb runs (`runVerb` sets `MESH_VERB` for every verb; the terminal has none). A change of other keys that keeps both as they were is still taken. - **The generic `command` verb** refuses `settings set` / `settings clear` lines outright (`argvFor`), pointing at the settings verb. - **Never at the machine's own trees, from anywhere (the terminal too):** `Places` and `AccessPlaces` clean the path and refuse at/below `/etc`, `/usr`, `/boot`, `/root`, `/run`, `/proc`, `/sys`, `/dev`, `/bin`, `/sbin`, `/lib*`, `/var/lib/mesh`, `/var/lib/mesh-host`, and exactly `/`, `/var`, `/var/lib`, `/home`, `/srv`, `/mnt`, `/opt`, `/tmp`, … . - **A setting is one line:** `JudgeSettings` (where a layer is kept and where it is composed) refuses `\n`, `\r`, NUL in any string or key at any depth. PEM blocks alone may hold lines (strict: base64 lines of 64, last padded), because step-ca serves its root to the route proxy as a setting. The co-located test's fake PEM fixture was 46 characters on its last line (not a whole number of base64 groups) and is now 48. **Tests that fail on main** (run there first): - `cmd/mesh-controller/terminal_settings_test.go` `TestPlacesAndAccessesAreRefusedThroughEveryVerb`: through `argvFor` + this binary's `run()` dispatch with `MESH_VERB` set as `runVerb` does — settings verb (one machine, whole mesh, move, drop with replace, clear) and `command` lines (three spellings, plus clear) are refused and the layer is unchanged; the terminal takes the placement; system paths refused at the terminal; line breaks refused. On main: `settings verb, one machine: <nil>`. - `internal/catalogue/terminal_settings_test.go`: system paths for places and accesses, one-line rule, PEM shape. On main: every system path taken, every line break taken. Live check before writing it: the 15 layers that set `places`/`accesses` today (all on the home server and the anchor's backup) pass the new refusal; details in the issue. `merge-check.sh` passes locally against a throwaway postgres (no C compiler is missing here: race-checked). hq: issue 339 (PR to follow).
mesh-admin added 1 commit 2026-10-08 22:00:25 +00:00
Keep places and accesses at the terminal, and refuse a line break in any setting
mesh/delivery delivered
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
f5824b31c6
Through the settings verb, or a settings line run by the generic command
verb, any caller of the mesh's console could place a module's directory at
/etc with an owner of its own and have the node-engine, as root, hand it
over at the next push, or mount any of the machine's paths into a container
(hq issue 339). A change to either key is now refused in every process a
verb runs, the generic verb refuses settings writes outright, and neither key
may name the machine's own trees from anywhere, the terminal included. A
line break, carriage return or NUL in any setting, which a file it is
written into reads as a line of the caller's own, is refused where a layer
is kept and where it is composed; PEM blocks alone may hold lines.
Author
Contributor

Delivery novox/mesh-controller@f5824b31c696 — delivered since 2026-10-08T22:35:42Z

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Machines

  • mesh-controller on novox: passed — healthy 3 times over 3m39s
  • mesh-controller on the rest running it: sent
  • build-agent on ace: passed — healthy 3 times over 3m31s
  • build-agent on the rest running it: sent
  • route-proxy on ace: passed — healthy 3 times over 3m41s
  • route-proxy on the rest running it: sent

Transitions

  • 2026-10-08 22:00 (new) → proposed (announced): novox/mesh-controller#168's head announced
  • 2026-10-08 22:06 proposed → checked (checked): the verdict names this commit
  • 2026-10-08 22:06 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-08 22:21 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-08 22:21 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
  • 2026-10-08 22:35 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-controller@f5824b31c696` — **delivered** since 2026-10-08T22:35:42Z **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves **Machines** - mesh-controller on novox: passed — healthy 3 times over 3m39s - mesh-controller on the rest running it: sent - build-agent on ace: passed — healthy 3 times over 3m31s - build-agent on the rest running it: sent - route-proxy on ace: passed — healthy 3 times over 3m41s - route-proxy on the rest running it: sent **Transitions** - 2026-10-08 22:00 (new) → proposed (announced): novox/mesh-controller#168's head announced - 2026-10-08 22:06 proposed → checked (checked): the verdict names this commit - 2026-10-08 22:06 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-08 22:21 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-08 22:21 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path - 2026-10-08 22:35 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Author
Contributor

Merge check at f5824b31

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
  • mesh/repo-check: PASS — its merge-check.sh passed

Delivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step

  • tier 0: mesh-controller
  • tier 1: build-agent
  • tier 2: route-proxy
  • ace: receives build-agent, route-proxy
  • g14: receives build-agent
  • novox: receives mesh-controller, build-agent, route-proxy
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • route-proxy provides route: its consumers are sent again after it
  • route-proxy keeps data (ADR 0233): what it holds is backed up before it moves

Run by the build seat on novox as build-1791496832099882991 (builds --log build-1791496832099882991).

--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 52 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T21:34:01Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T21:34:01Z

machines:
  - a machine (f35): composes
  - holds mesh-dns-resolver (idt): composes
  - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
  - a machine (yvbpsm): composes

a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok  	github.com/novox/mesh-controller/cmd/mesh-builder	1.049s
ok  	github.com/novox/mesh-controller/cmd/mesh-controller	211.458s
ok  	github.com/novox/mesh-controller/examples/objectstore-provisioner	1.018s
ok  	github.com/novox/mesh-controller/examples/postgres-provisioner	1.022s
ok  	github.com/novox/mesh-controller/examples/redis-provisioner	1.020s
ok  	github.com/novox/mesh-controller/examples/route-proxy	1.126s
ok  	github.com/novox/mesh-controller/internal/artifacts	1.053s
ok  	github.com/novox/mesh-controller/internal/broker	2.855s
ok  	github.com/novox/mesh-controller/internal/builder	2.663s
ok  	github.com/novox/mesh-controller/internal/catalogue	5.207s
ok  	github.com/novox/mesh-controller/internal/conditions	2.415s
ok  	github.com/novox/mesh-controller/internal/envfile	1.028s
ok  	github.com/novox/mesh-controller/internal/facts	1.027s
ok  	github.com/novox/mesh-controller/internal/identity	5.013s
ok  	github.com/novox/mesh-controller/internal/inventory	91.845s
ok  	github.com/novox/mesh-controller/internal/lease	17.671s
ok  	github.com/novox/mesh-controller/internal/licences	4.543s
ok  	github.com/novox/mesh-controller/internal/licences/adapters	1.015s
ok  	github.com/novox/mesh-controller/internal/link	54.476s
ok  	github.com/novox/mesh-controller/internal/lint	2.176s
ok  	github.com/novox/mesh-controller/internal/outward	1.042s
ok  	github.com/novox/mesh-controller/internal/overlay	1.020s
ok  	github.com/novox/mesh-controller/internal/secrets	1.510s
ok  	github.com/novox/mesh-controller/internal/store	3.411s
ok  	github.com/novox/mesh-controller/internal/testbus	1.186s
ok  	github.com/novox/mesh-controller/internal/token	1.020s
**Merge check** at `f5824b31` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy) - `mesh/repo-check`: **PASS** — its merge-check.sh passed **Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step - tier 0: mesh-controller - tier 1: build-agent - tier 2: route-proxy - ace: receives build-agent, route-proxy - g14: receives build-agent - novox: receives mesh-controller, build-agent, route-proxy - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - route-proxy provides route: its consumers are sent again after it - route-proxy keeps data (ADR 0233): what it holds is backed up before it moves Run by the build seat on novox as `build-1791496832099882991` (`builds --log build-1791496832099882991`). ``` --- the gate: build-agent, mesh-controller, route-proxy mesh-controller: ok, 52 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8) long-running resources without health: 1 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T21:34:01Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-08T21:34:01Z machines: - a machine (f35): composes - holds mesh-dns-resolver (idt): composes - holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes - a machine (yvbpsm): composes a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy --- its merge-check.sh (go toolchain) ok github.com/novox/mesh-controller/cmd/mesh-builder 1.049s ok github.com/novox/mesh-controller/cmd/mesh-controller 211.458s ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.018s ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.022s ok github.com/novox/mesh-controller/examples/redis-provisioner 1.020s ok github.com/novox/mesh-controller/examples/route-proxy 1.126s ok github.com/novox/mesh-controller/internal/artifacts 1.053s ok github.com/novox/mesh-controller/internal/broker 2.855s ok github.com/novox/mesh-controller/internal/builder 2.663s ok github.com/novox/mesh-controller/internal/catalogue 5.207s ok github.com/novox/mesh-controller/internal/conditions 2.415s ok github.com/novox/mesh-controller/internal/envfile 1.028s ok github.com/novox/mesh-controller/internal/facts 1.027s ok github.com/novox/mesh-controller/internal/identity 5.013s ok github.com/novox/mesh-controller/internal/inventory 91.845s ok github.com/novox/mesh-controller/internal/lease 17.671s ok github.com/novox/mesh-controller/internal/licences 4.543s ok github.com/novox/mesh-controller/internal/licences/adapters 1.015s ok github.com/novox/mesh-controller/internal/link 54.476s ok github.com/novox/mesh-controller/internal/lint 2.176s ok github.com/novox/mesh-controller/internal/outward 1.042s ok github.com/novox/mesh-controller/internal/overlay 1.020s ok github.com/novox/mesh-controller/internal/secrets 1.510s ok github.com/novox/mesh-controller/internal/store 3.411s ok github.com/novox/mesh-controller/internal/testbus 1.186s ok github.com/novox/mesh-controller/internal/token 1.020s ```
mesh-admin merged commit 522f2253e7 into main 2026-10-08 22:21:02 +00:00
mesh-admin deleted branch fix/339-places-and-accesses-are-the-terminals 2026-10-08 22:21:02 +00:00
This repo is archived. You cannot comment on pull requests.
No Reviewers
1 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#168