Dev VM user is a trusted nix user: a worker can switch the daemon's sandbox off #12

Closed
opened 2026-10-21 19:35:20 +01:00 by vnprc-agent · 3 comments

The dev VM user stops being a trusted nix user, so a process on the VM cannot change the daemon's substituters and trusted signing keys, map extra host paths into a build, or turn the build sandbox off, which today it can do silently.

Primary goals:

  • Drop the user from trusted-users — modules/guest-base.nix sets nix.settings.trusted-users = [ "root" username ] for every guest. Only root stays, unless a measured need appears (a flake nixConfig with extra substituters needs trust to be honoured; nothing in the public framework sets one).
  • Measure what breaks — nix build of each repo's checks, allod check, fleet-diff, and a verify-vm --repair from the hypervisor on a throwaway guest, before and after; any command that needed trust is named in the PR body with its replacement.
  • Why now — allod change run (allod/strategy#69) binds the nix daemon socket into a worker's sandbox so builds work inside, and the daemon trusts the connection by the user's uid. Measured on a dev VM: the daemon accepted --option sandbox false --option extra-sandbox-paths /home from the user with no "ignoring untrusted option" warning. What that does not reach: credentials are mode 0600 real files and the home directory is mode 0700, so the build users a sandbox-off build runs as were denied every read tried, sibling clones included. What it does reach: a trusted user can set substituters and trusted-public-keys for its own builds, so a worker can pull unsigned or attacker-signed outputs for a derivation into the shared store, where a later build of the same derivation by the overseer reuses them. That is a store-poisoning path, and it exists today for any process on the VM, sandboxed or not.

Scope

In: the one line in modules/guest-base.nix, the measurements, and the guest module contract check if it pins trusted users. Out: the hypervisor, whose trust settings belong to nexus; the sandbox command itself (allod/tools#282).

The dev VM user stops being a trusted nix user, so a process on the VM cannot change the daemon's substituters and trusted signing keys, map extra host paths into a build, or turn the build sandbox off, which today it can do silently. Primary goals: - **Drop the user from `trusted-users`** — `modules/guest-base.nix` sets `nix.settings.trusted-users = [ "root" username ]` for every guest. Only `root` stays, unless a measured need appears (a flake `nixConfig` with extra substituters needs trust to be honoured; nothing in the public framework sets one). - **Measure what breaks** — `nix build` of each repo's checks, `allod check`, `fleet-diff`, and a `verify-vm --repair` from the hypervisor on a throwaway guest, before and after; any command that needed trust is named in the PR body with its replacement. - **Why now** — `allod change run` (allod/strategy#69) binds the nix daemon socket into a worker's sandbox so builds work inside, and the daemon trusts the connection by the user's uid. Measured on a dev VM: the daemon accepted `--option sandbox false --option extra-sandbox-paths /home` from the user with no "ignoring untrusted option" warning. What that does not reach: credentials are mode 0600 real files and the home directory is mode 0700, so the build users a sandbox-off build runs as were denied every read tried, sibling clones included. What it does reach: a trusted user can set `substituters` and `trusted-public-keys` for its own builds, so a worker can pull unsigned or attacker-signed outputs for a derivation into the shared store, where a later build of the same derivation by the overseer reuses them. That is a store-poisoning path, and it exists today for any process on the VM, sandboxed or not. ### Scope In: the one line in `modules/guest-base.nix`, the measurements, and the guest module contract check if it pins trusted users. Out: the hypervisor, whose trust settings belong to `nexus`; the sandbox command itself (allod/tools#282).
Author

Handoff: two commits on agent/untrusted-nix-user (drop trust; authorize root for closure delivery), verified; PR body:

Every guest stops trusting its login user with the nix daemon, and the hypervisor delivers a rebuilt system to a guest as root instead of as that user. What changes on a running machine: a process on the guest can no longer hand the daemon privileged options such as a post-build hook that runs as root, a sandbox-off build, or its own substituters and signing keys; root accepts the hypervisor's SSH keys, with password login prohibited as before. What stays identical: the login user keeps passwordless sudo, every check and build the framework runs as that user still works, and the hypervisor's authority over guests is unchanged since it already held root through sudo. Proof: the module asserts both settings and two sabotage fixtures show the assertions failing; every archetypes check builds against this commit; all four guests change derivation and the hypervisor and installer do not. If it is wrong, revert; a guest rebuilt from the previous pin is back to the old trust. This lands together with the allod/nexus change that points rebuild-vm at root, or rebuilds fail on unsigned paths.

Refs allod/strategy#69. Closes allod/vm#12.

Risk

R3. Changes who the daemon trusts and who may log in as root on every guest; generated sshd and nix configuration; a cross-repo ordering with allod/nexus. No secrets move. Rollback is a pin revert and a rebuild. The worst credible failure after validation is a guest that cannot receive a rebuild because the hypervisor still targets the user: the paired nexus change is the guard, and the first rebuild after landing is the live witness.

Validation

  • allod check in the branch: all steps pass; the mutation log shows trusted-user-reinstated, root-without-keys and permit-root-login-password each rejected with the module's message.
  • allod check --override-input vm <this commit> in allod/archetypes: All 46 steps passed (checks: 30 built).
  • Toplevel derivation per machine with the vm input overridden: allod-dev, bitcoin-node, forge, privacy-1 change; nexus and installer do not.
  • Survey of every public flake and every nexus and tools script for settings that need a trusted user: none found; the one hit is a test proving an untrusted flake config is declined.
  • Not run here: a live rebuild of a guest through the changed path, which is host-only.

Paired allod/nexus change, one commit on agent/rebuild-as-root; its PR body:

rebuild-vm delivers a guest's new system as root over SSH instead of as the guest's login user with sudo. Nothing else on the hypervisor changes; verify-vm, provision-vm and the bootstrap still reach guests as before. The reason is the paired allod/vm change that drops the login user from the daemon's trusted users: nix refuses unsigned store paths from an untrusted user, so a rebuild copied as that user would fail, and root stays trusted. Proof: the script's tests pass with the new target, every nexus check passes, and the built provisioning script directory differs from the previous commit in exactly this one file and only in this one line. If it is wrong, revert; the old line works against any guest still trusting its user.

Refs allod/strategy#69. Refs allod/vm#12.

Risk

R2. One flag change in one host script; the hypervisor derivation moves, as it does for every nexus commit, and the built script tree was diffed to show the change is this line alone. Rollback is a revert. It must land with, or after, the allod/vm change: before it, root has no authorized keys on a guest and the rebuild fails at SSH, which is loud and leaves the guest untouched.

Validation

  • bash tests/rebuild-vm.sh: 17 passed. allod check: All 16 steps passed.
  • packages.x86_64-linux.provisioning-scripts built before and after: diff -rq reports bin/rebuild-vm only, and its diff is the target-host line.
  • Other scripts audited for a closure copy as the user: rebuild-nexus is local, provision-vm already targets root, the bootstrap copies git repositories only.
  • Not run here: a live rebuild of a guest, which is host-only.
Handoff: two commits on `agent/untrusted-nix-user` (drop trust; authorize root for closure delivery), verified; PR body: Every guest stops trusting its login user with the nix daemon, and the hypervisor delivers a rebuilt system to a guest as root instead of as that user. What changes on a running machine: a process on the guest can no longer hand the daemon privileged options such as a post-build hook that runs as root, a sandbox-off build, or its own substituters and signing keys; root accepts the hypervisor's SSH keys, with password login prohibited as before. What stays identical: the login user keeps passwordless sudo, every check and build the framework runs as that user still works, and the hypervisor's authority over guests is unchanged since it already held root through sudo. Proof: the module asserts both settings and two sabotage fixtures show the assertions failing; every archetypes check builds against this commit; all four guests change derivation and the hypervisor and installer do not. If it is wrong, revert; a guest rebuilt from the previous pin is back to the old trust. This lands together with the allod/nexus change that points rebuild-vm at root, or rebuilds fail on unsigned paths. Refs allod/strategy#69. Closes allod/vm#12. ## Risk R3. Changes who the daemon trusts and who may log in as root on every guest; generated sshd and nix configuration; a cross-repo ordering with allod/nexus. No secrets move. Rollback is a pin revert and a rebuild. The worst credible failure after validation is a guest that cannot receive a rebuild because the hypervisor still targets the user: the paired nexus change is the guard, and the first rebuild after landing is the live witness. ## Validation - `allod check` in the branch: all steps pass; the mutation log shows `trusted-user-reinstated`, `root-without-keys` and `permit-root-login-password` each rejected with the module's message. - `allod check --override-input vm <this commit>` in `allod/archetypes`: `All 46 steps passed (checks: 30 built)`. - Toplevel derivation per machine with the vm input overridden: `allod-dev`, `bitcoin-node`, `forge`, `privacy-1` change; `nexus` and `installer` do not. - Survey of every public flake and every nexus and tools script for settings that need a trusted user: none found; the one hit is a test proving an untrusted flake config is declined. - Not run here: a live rebuild of a guest through the changed path, which is host-only. --- Paired allod/nexus change, one commit on `agent/rebuild-as-root`; its PR body: `rebuild-vm` delivers a guest's new system as root over SSH instead of as the guest's login user with sudo. Nothing else on the hypervisor changes; `verify-vm`, `provision-vm` and the bootstrap still reach guests as before. The reason is the paired allod/vm change that drops the login user from the daemon's trusted users: nix refuses unsigned store paths from an untrusted user, so a rebuild copied as that user would fail, and root stays trusted. Proof: the script's tests pass with the new target, every nexus check passes, and the built provisioning script directory differs from the previous commit in exactly this one file and only in this one line. If it is wrong, revert; the old line works against any guest still trusting its user. Refs allod/strategy#69. Refs allod/vm#12. ## Risk R2. One flag change in one host script; the hypervisor derivation moves, as it does for every nexus commit, and the built script tree was diffed to show the change is this line alone. Rollback is a revert. It must land with, or after, the allod/vm change: before it, root has no authorized keys on a guest and the rebuild fails at SSH, which is loud and leaves the guest untouched. ## Validation - `bash tests/rebuild-vm.sh`: 17 passed. `allod check`: `All 16 steps passed`. - `packages.x86_64-linux.provisioning-scripts` built before and after: `diff -rq` reports `bin/rebuild-vm` only, and its diff is the target-host line. - Other scripts audited for a closure copy as the user: `rebuild-nexus` is local, `provision-vm` already targets root, the bootstrap copies git repositories only. - Not run here: a live rebuild of a guest, which is host-only.
Author

Migration note, found on the first rebuild after landing: a guest built before this change has no authorized keys for root, so the new rebuild-vm fails at SSH against it. Each already-running guest needs one delivery by the old path: build the system (rebuild-vm prints the closure path before failing at the copy), nix-copy-closure it to the guest as the login user, which the old running system still trusts, and switch it there with sudo. From then on rebuild-vm works as root. New guests provisioned after the change need nothing.

Migration note, found on the first rebuild after landing: a guest built before this change has no authorized keys for root, so the new rebuild-vm fails at SSH against it. Each already-running guest needs one delivery by the old path: build the system (rebuild-vm prints the closure path before failing at the copy), nix-copy-closure it to the guest as the login user, which the old running system still trusts, and switch it there with sudo. From then on rebuild-vm works as root. New guests provisioned after the change need nothing.
Author

Landed with the paired nexus change; nexus, nix-dev, allod-dev and rust-dev rebuilt through root delivery. Remaining guests take the one-time delivery in the comment above when next rebuilt. Closing root's SSH door again is allod/vm#13.

Landed with the paired nexus change; nexus, nix-dev, allod-dev and rust-dev rebuilt through root delivery. Remaining guests take the one-time delivery in the comment above when next rebuilt. Closing root's SSH door again is allod/vm#13.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/vm#12
No description provided.