deploy: make the local reinstall path actually work, and fail closed
The `install <config> localhost` auto-path added in 0ea9020 could not have
completed. Fixed three blockers plus the guard that was silently not guarding.
Inside installer-iso the run died before doing anything:
- systemd sets no $HOME for a service without User= (SetLoginEnvironment=
defaults to false), and this script runs under `set -u`, so it aborted on
the bare $HOME with "unbound variable". Added $KEYDIR + Environment=HOME.
- the host key it needs to seed /etc/ssh isn't on the ISO at all — that is
built from a public repo and carries no credentials on purpose. It now
travels on the boot partition, located via homelab.keypart=<PARTUUID> on
the kernel cmdline, and dies with the disko wipe minutes later. Without
it sops can't decrypt on boot #1 and mutableUsers locks darman for good.
- installation-cd-minimal leaves experimental-features unset, so both
`nix run` and `nixos-install --flake` failed. (The nixos-images kexec
installer sets them itself, which is why the same branch worked after
kexec-local but not from the ISO.)
The staging-dir guard passed everything on btrfs: findmnt prints the
subvolume as /dev/sdb2[/@], lsblk can't open that, and an empty parent was
treated as "different disk" — so it allowed staging the iso on the very disk
disko then wiped. terra's current CachyOS root is exactly that layout. Now
uses --nofsroot, resolves EVERY whole-disk ancestor (LVM/RAID span several:
/mnt/ssd_01 -> sdd+sde), and treats "can't tell" as a hard error. btrfs
staging is refused outright — stage-1 mounts a btrfs volume's top level, so
an iso inside a subvolume is unreachable.
findiso= lost its leading slash whenever the staging mountpoint was /,
giving /findisovar/tmp/x.iso and an emergency shell after the reboot.
Also:
- confirm before rebooting, like flash/kexec-local already do; --yes skips
it and is what the ISO passes itself
- $BOOT from `bootctl --print-boot-path`, not a hardcoded /boot
- free-space checks on both target partitions before the ~1GB copy
- `nix run .#disko` / `.#nixos-anywhere` from locked inputs instead of
github:... master-of-the-day, resolved while a disk is being wiped
- one_match warns instead of silently taking [0]; require_tracked covers
every hosts/<config>/*.nix; flash traps its mount
- drop nixos-images' `inputs.nixpkgs.follows` — that input doesn't exist,
it only printed a warning on every nix command
Verified: the prepare path exercised under stubs against this box's real
disks (btrfs-on-OS-disk, tmpfs, LVM, subdirectory), shellcheck clean, all
six configs evaluate, checks.kexec-local still passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -125,11 +125,42 @@ kept its ssh host key. Run it after ANY change to the kexec paths.
|
||||
loop: it reads `homelab.install=<config>` back off `/proc/cmdline` (set by the prepare
|
||||
step) and re-runs the identical command itself once `homelab-checkout.service` has
|
||||
cloned the repo — the whole reinstall is one command and unattended after the first
|
||||
reboot. It always ASKS where to stage the iso file (never auto-picks — the wrong disk
|
||||
here is destroyed mid-install) and refuses if that turns out to be the disk
|
||||
`disk-config.nix` is about to wipe; `HOMELAB_INSTALLER_STAGE_DIR` skips the prompt for
|
||||
scripted use. Both this and `kexec-local` self-elevate via `sudo` (`require_root()`)
|
||||
rather than requiring you to prefix the command yourself.
|
||||
reboot. It confirms (type `yes`) before rebooting, like `flash`/`kexec-local`; `--yes`
|
||||
skips that and is what the ISO passes itself. It always ASKS where to stage the iso
|
||||
file (never auto-picks — the wrong disk here is destroyed mid-install);
|
||||
`HOMELAB_INSTALLER_STAGE_DIR` skips the prompt for scripted use. Both this and
|
||||
`kexec-local` self-elevate via `sudo` (`require_root()`) rather than requiring you to
|
||||
prefix the command yourself.
|
||||
- **The staging-dir guard must fail CLOSED, and `findmnt` needs `--nofsroot`**: on btrfs
|
||||
`findmnt -no SOURCE` prints `/dev/sdb2[/@]`, which `lsblk` cannot open, so a naive
|
||||
parent-device lookup comes back empty. Treating empty as "different disk" silently
|
||||
allowed staging the iso on the very disk disko then wiped — terra's CachyOS root is
|
||||
exactly that layout, so it hit the live case. `disks_backing()` (`lsblk -rnso
|
||||
NAME,TYPE`) returns EVERY whole-disk ancestor because LVM/RAID can span several
|
||||
(`/mnt/ssd_01` → sdd + sde), and an empty result is a hard error, not a pass. btrfs
|
||||
staging is refused outright: stage-1 mounts a btrfs volume's TOP level, so an iso
|
||||
inside a subvolume is unreachable via `findiso=`.
|
||||
- **`findiso=` must keep its leading slash**: stage-1 tests `[ -e "/findiso$isoPath" ]`,
|
||||
so stripping the mountpoint prefix off a stagedir whose mountpoint is `/` yields
|
||||
`var/tmp/x.iso` → `/findisovar/tmp/x.iso` → emergency shell, after you have already
|
||||
rebooted out of the working OS.
|
||||
- **The auto-install needs the host key shipped to it, and a `$HOME`**: the ISO is built
|
||||
from a public repo with no credentials, so `local_install_prepare_and_reboot()` copies
|
||||
the key onto the boot partition and passes that partition's PARTUUID as
|
||||
`homelab.keypart=`; the service mounts it and drops the key in
|
||||
`/root/.config/homelab/<config>/` before running the install. Also, systemd does NOT
|
||||
set `$HOME` for a system service without `User=` (`SetLoginEnvironment=` defaults to
|
||||
false), and `scripts/deploy` runs under `set -u` — hence `$KEYDIR` instead of a bare
|
||||
`$HOME`, plus `Environment=HOME=/root` on the unit.
|
||||
- **`installer-iso` must enable `experimental-features` itself.** `installation-cd-minimal`
|
||||
leaves them unset, so `nix run` and `nixos-install --flake` both die with "experimental
|
||||
Nix feature 'nix-command' is disabled". The nixos-images `kexec` installer sets them
|
||||
itself, which is why the same `install <config> localhost` branch worked after
|
||||
`kexec-local` but not from the ISO.
|
||||
- **disko/nixos-anywhere run as `nix run .#disko` / `.#nixos-anywhere`**, from this
|
||||
flake's locked inputs — not `nix run github:...`. They execute while a disk is being
|
||||
wiped, so the revision must be the reviewed one in `flake.lock`, and it has to resolve
|
||||
without network.
|
||||
- **disko wipes only the OS disk** named in `hosts/<h>/disk-config.nix`; data disks are
|
||||
plain `fileSystems` in `configuration.nix`.
|
||||
- `nixos-anywhere`/kexec needs a writable root; **ZimaOS root is read-only**, hence the
|
||||
|
||||
Reference in New Issue
Block a user