mkServiceVm: root-only guest with /var/lib on the data disk, LAN-only by default, and the Forgejo package pin #137

Closed
opened 2026-10-07 02:21:09 +01:00 by vnprc-agent · 1 comment

mkServiceVm is the fourth builder: a hypervisor guest with no operator account, administered as root over the hypervisor's keys, its whole /var/lib on the data disk the hypervisor attaches so a reprovision keeps the service's state, and a firewall that admits LAN peers on the ports the profile opens and no outbound traffic unless the profile widens it. Part of allod/strategy#65 (the forge VM is the first such machine) and the hypervisor-placed first step of allod/strategy#45's service archetype. allod/archetypes#60 was a rented-host attempt under the same name and is not reused: a service VM is a guest, so it composes sharedModules like dev and privacy VMs.

Primary goals:

  • Root only — no user account, no home-manager; users.users.root.openssh.authorizedKeys.keys is the builder's hostPublicKeys (pinned by machineConfigurations to the hypervisor's keys, as for every guest); sshd key-only with PermitRootLogin = "prohibit-password". A definition declaring homeModules is an evaluation error naming why, the one place this archetype could fall back silently.
  • The data disk is /var/lib — the builder declares fileSystems."/var/lib" on /dev/disk/by-id/virtio-allod-data (the serial allod/nexus attaches as vdb), ext4, formatted by the guest on first boot (autoFormat), neededForBoot so activation's uid map in /var/lib/nixos lands on it and file ownership survives a reinstall by construction. A service machine whose inventory entry lacks data_disk is refused by the builder as well as by inventory.
  • LAN only by default — the firewall is on, the builder opens nothing beyond administrative ssh, and outbound connections to addresses outside the guest's own network are refused; a profile opens the ports its service listens on and may set the egress mode to open (the second service VM syncs on clearnet before going Tor-only, so this is a profile's decision, not the type's). Pick the mechanism by what the VM test can witness, not by what reads well.
  • Forgejo tracks the channel's current release — modules/forgejo sets services.forgejo.package = lib.mkDefault pkgs.forgejo, never nixpkgs' LTS default, so a template forge runs the same Forgejo a deployment's VPS pins and a dump always restores onto an equal or newer version; checks/forgejo.nix asserts the booted version is that package's.
  • Identities and facts — profileArchetypes and builders gain service; mkIdentities takes serviceIdentities (read as secrets.lib.serviceIdentities or {}); vmFacts includes service machines with username from that identity, which is root.
  • Witness — an eval check driving builders.service with a fixture and forcing its toplevel, with sabotage for the homeModules refusal and the missing-data-disk refusal; and a VM test: a service node with a second drive carrying serial allod-data (virtualisation.qemu.drives with deviceExtraOpts.serial) and a peer node, where first boot formats and mounts the drive at /var/lib (findmnt names the device), /var/lib/nixos is on it, a file written there survives a reboot, root logs in on the administrative port with the fixture hypervisor key and a second valid key is refused with a publickey denial, no login account other than root exists, the peer reaches a port the fixture profile opens and is refused on one it does not, and the node's outbound connection to an address outside its network is refused while one to the peer succeeds.

Current state and specifics

flake.nix maps builders = { dev; privacy; hypervisor; }, profileArchetypes = [ "dev" "privacy" "hypervisor" ], and mkIdentities { devIdentities, privacyIdentities, hypervisorIdentities }; nix/builders/privacy.nix is the closest sibling (sharedModules, runtime and platform defaulting to inventory, home-manager for the identity's user) and the new builder is that file minus home-manager and the user, plus the three things above. nix/vm-facts.nix includeMachine is type != "hypervisor", so service machines are already included once inventory accepts them; requireAttr vm "username" reads vmUsernames, which secrets will supply as root. checks/public-host.nix and checks/static-site.nix are the shape for the eval check and the VM test; checks/forgejo.nix for a two-node test with a custody grep. A framework check reads fixtures it builds, never a template machine by name (README, Fork-safe checks).

Scope

In: the builder, the flake wiring, the Forgejo package pin and its assertion, the two checks, README (Builders and Exports). Out: the forge machine, profile and secrets entry (allod/strategy#65's next slice); anything in allod/nexus, which needs no change because its scripts log in as the identity's username and provisioning installs as root already; at-rest encryption of guest disks (allod/archetypes#135).

`mkServiceVm` is the fourth builder: a hypervisor guest with no operator account, administered as root over the hypervisor's keys, its whole `/var/lib` on the data disk the hypervisor attaches so a reprovision keeps the service's state, and a firewall that admits LAN peers on the ports the profile opens and no outbound traffic unless the profile widens it. Part of allod/strategy#65 (the forge VM is the first such machine) and the hypervisor-placed first step of allod/strategy#45's service archetype. allod/archetypes#60 was a rented-host attempt under the same name and is not reused: a service VM is a guest, so it composes `sharedModules` like dev and privacy VMs. Primary goals: - **Root only** — no user account, no home-manager; `users.users.root.openssh.authorizedKeys.keys` is the builder's `hostPublicKeys` (pinned by `machineConfigurations` to the hypervisor's keys, as for every guest); sshd key-only with `PermitRootLogin = "prohibit-password"`. A definition declaring `homeModules` is an evaluation error naming why, the one place this archetype could fall back silently. - **The data disk is `/var/lib`** — the builder declares `fileSystems."/var/lib"` on `/dev/disk/by-id/virtio-allod-data` (the serial `allod/nexus` attaches as `vdb`), ext4, formatted by the guest on first boot (`autoFormat`), `neededForBoot` so activation's uid map in `/var/lib/nixos` lands on it and file ownership survives a reinstall by construction. A service machine whose inventory entry lacks `data_disk` is refused by the builder as well as by inventory. - **LAN only by default** — the firewall is on, the builder opens nothing beyond administrative ssh, and outbound connections to addresses outside the guest's own network are refused; a profile opens the ports its service listens on and may set the egress mode to open (the second service VM syncs on clearnet before going Tor-only, so this is a profile's decision, not the type's). Pick the mechanism by what the VM test can witness, not by what reads well. - **Forgejo tracks the channel's current release** — `modules/forgejo` sets `services.forgejo.package = lib.mkDefault pkgs.forgejo`, never nixpkgs' LTS default, so a template forge runs the same Forgejo a deployment's VPS pins and a dump always restores onto an equal or newer version; `checks/forgejo.nix` asserts the booted version is that package's. - **Identities and facts** — `profileArchetypes` and `builders` gain `service`; `mkIdentities` takes `serviceIdentities` (read as `secrets.lib.serviceIdentities or {}`); `vmFacts` includes service machines with `username` from that identity, which is `root`. - **Witness** — an eval check driving `builders.service` with a fixture and forcing its toplevel, with sabotage for the `homeModules` refusal and the missing-data-disk refusal; and a VM test: a service node with a second drive carrying serial `allod-data` (`virtualisation.qemu.drives` with `deviceExtraOpts.serial`) and a peer node, where first boot formats and mounts the drive at `/var/lib` (`findmnt` names the device), `/var/lib/nixos` is on it, a file written there survives a reboot, root logs in on the administrative port with the fixture hypervisor key and a second valid key is refused with a publickey denial, no login account other than root exists, the peer reaches a port the fixture profile opens and is refused on one it does not, and the node's outbound connection to an address outside its network is refused while one to the peer succeeds. ### Current state and specifics `flake.nix` maps `builders = { dev; privacy; hypervisor; }`, `profileArchetypes = [ "dev" "privacy" "hypervisor" ]`, and `mkIdentities { devIdentities, privacyIdentities, hypervisorIdentities }`; `nix/builders/privacy.nix` is the closest sibling (sharedModules, `runtime` and `platform` defaulting to inventory, home-manager for the identity's user) and the new builder is that file minus home-manager and the user, plus the three things above. `nix/vm-facts.nix` `includeMachine` is `type != "hypervisor"`, so service machines are already included once inventory accepts them; `requireAttr vm "username"` reads `vmUsernames`, which secrets will supply as `root`. `checks/public-host.nix` and `checks/static-site.nix` are the shape for the eval check and the VM test; `checks/forgejo.nix` for a two-node test with a custody grep. A framework check reads fixtures it builds, never a template machine by name (README, Fork-safe checks). ### Scope In: the builder, the flake wiring, the Forgejo package pin and its assertion, the two checks, README (Builders and Exports). Out: the `forge` machine, profile and secrets entry (allod/strategy#65's next slice); anything in `allod/nexus`, which needs no change because its scripts log in as the identity's username and provisioning installs as root already; at-rest encryption of guest disks (allod/archetypes#135).
Author

Landed on master as a40fab0.

Landed on master as a40fab0.
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/archetypes#137
No description provided.