From 04e04cd8392816db05dc9f585b4f935763344dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyle=20=F0=9F=90=86?= Date: Sun, 12 Jul 2026 08:59:00 -0400 Subject: [PATCH 1/2] tests: end-to-end install-keepnode install-mechanics check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kyle 🐆 --- flake.nix | 28 ++++++++++++++ tests/install-keepnode.nix | 79 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 tests/install-keepnode.nix diff --git a/flake.nix b/flake.nix index 455f5d7..b3eaa81 100644 --- a/flake.nix +++ b/flake.nix @@ -712,6 +712,34 @@ }).config.system.build.toplevel; }; }; + # End-to-end SUCCESSFUL install of the real bring-up appliance (installer-guards above only + # covers the abort paths with a stubbed closure): partition + nixos-install + key enrollment, + # asserted against the installed /mnt. The installed closure is the real keepnodeBringupSystem + # with one test-only accommodation -- canTouchEfiVariables=false -- so nixos-install's bootctl + # needs no efivarfs in the BIOS installer VM (the appliance ships it true for the real box). + # Heavy (nested install of the full appliance closure); runs in the full-CI subset only. + install-keepnode = pkgs.testers.runNixOSTest { + imports = [ ./tests/install-keepnode.nix ]; + _module.args = { + inherit adminKeyFixture; + keepnodeToplevel = + (nixpkgs.lib.nixosSystem { + inherit system; + modules = [ + ./nixos/keep-node.nix + ./nixos/appliance.nix + { + keepNode.adminAccess = { + enable = true; + authorizedKeysFile = "/etc/keepnode/admin_authorized_keys"; + lanBringup = true; + }; + boot.loader.efi.canTouchEfiVariables = nixpkgs.lib.mkForce false; + } + ]; + }).config.system.build.toplevel; + }; + }; wisp-mesh = pkgs.testers.runNixOSTest { imports = [ ./tests/wisp-mesh.nix ]; _module.args = { diff --git a/tests/install-keepnode.nix b/tests/install-keepnode.nix new file mode 100644 index 0000000..6172311 --- /dev/null +++ b/tests/install-keepnode.nix @@ -0,0 +1,79 @@ +# tests/install-keepnode.nix -- run install-keepnode end-to-end on a blank disk and prove the install +# MECHANICS succeed: partitioning, nixos-install of the REAL bring-up appliance closure, systemd-boot +# written to the ESP, and the operator key enrolled into the runtime authorizedKeysFile. +# +# installer-guards.nix covers only the abort paths (with a stubbed closure); this runs a SUCCESSFUL +# install of the real keepnodeBringupSystem. It asserts against the freshly-installed /mnt rather than +# booting the installed disk: the actual boot is firmware/TPM-specific (validated on real hardware), +# while the environment-independent install mechanics -- which were never success-tested before -- are +# proven here deterministically in a single VM (no second-boot/OVMF handoff to flake on). +# +# The installed closure (keepnodeToplevel arg) is the real bring-up appliance with ONE test-only +# accommodation, canTouchEfiVariables=false, so nixos-install's `bootctl install` needs no efivarfs in +# the BIOS installer VM (the appliance ships it true for the real UEFI box). Nothing else differs, so +# the install path and the installed closure are otherwise exactly what ships. +# +# Run: nix build .#checks.x86_64-linux.install-keepnode +{ + adminKeyFixture, + keepnodeToplevel, + ... +}: +{ + name = "keep-node-install-keepnode"; + + nodes.machine = + { pkgs, ... }: + { + imports = [ ../nixos/installer.nix ]; + _module.args.keepnodeToplevel = keepnodeToplevel; + # install-keepnode is written for the installation-CD environment, which provides `nixos-install` + # on PATH (installer.nix intentionally leaves it unpinned) and an existing /mnt mount point. This + # bare test node has neither, so provide the same two things the CD would (mirroring reality + # rather than special-casing the installer). + environment.systemPackages = [ pkgs.nixos-install-tools ]; + systemd.tmpfiles.rules = [ "d /mnt 0755 root root - -" ]; + # A blank second disk (/dev/vdb) is the install TARGET. The installer itself boots off the + # default test root (host store), so no rootDevice/bootloader wiring is needed to run the + # install -- the target only has to be a blank whole disk install-keepnode can partition. + virtualisation.emptyDiskImages = [ (12 * 1024) ]; # must hold the copied appliance closure + virtualisation.memorySize = 4096; + virtualisation.cores = 4; + }; + + testScript = '' + machine.start() + machine.wait_for_unit("multi-user.target") + machine.succeed("udevadm settle") + + # Non-interactive YES; install onto the blank target /dev/vdb, reading the operator pubkey from a + # FILE (exercises install-keepnode's --ssh-key read path). + machine.succeed( + "printf 'YES\n' | install-keepnode /dev/vdb --ssh-key ${adminKeyFixture}/id.pub >&2" + ) + + # Partitioning: the GPT ESP + nixos partitions exist with the labels install-keepnode assigns. + machine.succeed("blkid -L ESP") + machine.succeed("blkid -L nixos") + + # nixos-install installed EXACTLY the closure install-keepnode was handed (the real bring-up + # appliance): the target's system profile resolves to that store path. + installed = machine.succeed("readlink -f /mnt/nix/var/nix/profiles/system").strip() + assert installed == "${keepnodeToplevel}", ( + f"installed system profile {installed!r} != the shipped bring-up closure ${keepnodeToplevel}" + ) + + # systemd-boot landed on the ESP with generated boot entries -> the target is bootable. + machine.succeed("test -e /mnt/boot/EFI/systemd/systemd-bootx64.efi") + machine.succeed("test -n \"$(ls -A /mnt/boot/loader/entries 2>/dev/null)\"") + + # The operator key was enrolled into the runtime authorizedKeysFile that keepNode.adminAccess + # reads at boot: the exact pubkey bytes, world-readable (0644) as the module expects. + machine.succeed( + "grep -qF \"$(cat ${adminKeyFixture}/id.pub)\" /mnt/etc/keepnode/admin_authorized_keys" + ) + machine.succeed( + "test \"$(stat -c %a /mnt/etc/keepnode/admin_authorized_keys)\" = 644" + ) + ''; +} From 4437d8fbc9b63bfb9ab46d9b9c306e07713c55a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyle=20=F0=9F=90=86?= Date: Sun, 12 Jul 2026 09:12:24 -0400 Subject: [PATCH 2/2] tests: derive install-keepnode closure from keepnodeBringupSystem (drift guard) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kyle 🐆 --- flake.nix | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/flake.nix b/flake.nix index b3eaa81..41f9c0c 100644 --- a/flake.nix +++ b/flake.nix @@ -722,21 +722,12 @@ imports = [ ./tests/install-keepnode.nix ]; _module.args = { inherit adminKeyFixture; + # Derive from the SHIPPED bring-up system (not a hand-copy) so the single test-only override + # is provably the only divergence: any future change to keepnodeBringupSystem reaches this + # test automatically, keeping the "installs the real closure" guarantee from rotting. keepnodeToplevel = - (nixpkgs.lib.nixosSystem { - inherit system; - modules = [ - ./nixos/keep-node.nix - ./nixos/appliance.nix - { - keepNode.adminAccess = { - enable = true; - authorizedKeysFile = "/etc/keepnode/admin_authorized_keys"; - lanBringup = true; - }; - boot.loader.efi.canTouchEfiVariables = nixpkgs.lib.mkForce false; - } - ]; + (keepnodeBringupSystem.extendModules { + modules = [ { boot.loader.efi.canTouchEfiVariables = nixpkgs.lib.mkForce false; } ]; }).config.system.build.toplevel; }; };