From 8dddb1588d64965c676ce4055bfccb6a776af76a Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Tue, 16 Dec 2025 10:42:50 -0600 Subject: [PATCH] trying out installer again --- hosts/i001/flake.nix | 2 + hosts/i001/hardware-mounts.nix | 294 ++++++++++-------- .../nixos-installers/install_bcachefs.md | 5 +- 3 files changed, 162 insertions(+), 139 deletions(-) diff --git a/hosts/i001/flake.nix b/hosts/i001/flake.nix index f1d0e684..0312b734 100644 --- a/hosts/i001/flake.nix +++ b/hosts/i001/flake.nix @@ -77,6 +77,8 @@ system.stateVersion = stateAndHomeVersion; # TODO allowing password auth for now services.openssh.settings.PasswordAuthentication = lib.mkForce true; + # TODO remove this for testbed + security.sudo.wheelNeedsPassword = false; # Home Manager home-manager = { diff --git a/hosts/i001/hardware-mounts.nix b/hosts/i001/hardware-mounts.nix index 646fc0c8..712973ef 100644 --- a/hosts/i001/hardware-mounts.nix +++ b/hosts/i001/hardware-mounts.nix @@ -6,15 +6,14 @@ ... }: let + BOOT = "/dev/disk/by-uuid/ABDB-2A38"; + PRIMARY = "/dev/disk/by-uuid/08610781-26d3-456f-9026-35dd4a40846f"; + + SWAP = "/dev/disk/by-uuid/e4b53a74-6fde-4e72-b925-be3a249e37be"; + USB_KEY = "/dev/disk/by-uuid/63a7bd87-d644-43ea-83ba-547c03012fb6"; - BOOT = "/dev/disk/by-uuid/ABDB-2A38"; - PRIMARY_UUID = "08610781-26d3-456f-9026-35dd4a40846f"; - PRIMARY = "/dev/disk/by-uuid/${PRIMARY_UUID}"; - - inherit (utils) escapeSystemdPath; - - primaryDeviceUnit = "${escapeSystemdPath PRIMARY}.device"; + primaryDeviceUnit = "${utils.escapeSystemdPath PRIMARY}.device"; in lib.mkMerge [ # Main filesystems @@ -35,15 +34,8 @@ lib.mkMerge [ fsType = "bcachefs"; options = [ "X-mount.subdir=@root" - ]; - }; - fileSystems."/.old_roots" = { - device = PRIMARY; - fsType = "bcachefs"; - options = [ - "nofail" # this may not exist yet just skip it - "X-mount.mkdir" - "X-mount.subdir=@old_roots" + # "x-systemd.requires=unlock-bcachefs-custom.service" + # "x-systemd.after=unlock-bcachefs-custom.service" ]; }; fileSystems."/nix" = { @@ -60,7 +52,7 @@ lib.mkMerge [ fsType = "bcachefs"; options = [ "X-mount.mkdir" - "X-mount.subdir=@root" + "X-mount.subdir=@snapshots" "relatime" ]; }; @@ -74,15 +66,8 @@ lib.mkMerge [ ]; }; } - # SWAP - { - swapDevices = [ - # { - # device = "/.swap/swapfile"; - # size = 8 * 1024; # Creates an 8GB swap file - # } - ]; - } + # SWAP (optional) + { swapDevices = [ { device = SWAP; } ]; } # Disable bcachefs built in password prompts for all mounts (which asks for every single subdir mount above ( let @@ -132,25 +117,21 @@ lib.mkMerge [ "bcachefs" ]; - boot.initrd.systemd.mounts = [ - { - what = USB_KEY; - type = "bcachefs"; - where = "/usb_key"; - options = "ro"; - description = "key"; - wantedBy = [ - "initrd.target" - "initrd-root-fs.target" - ]; - } - ]; - boot.initrd.systemd.services."sysroot.mount" = { - unitConfig = { - Requires = [ "unlock-bcachefs-custom.service" ]; - After = [ "unlock-bcachefs-custom.service" ]; - }; - }; + # From a USB key # NOTE this method does work but if you want to boot w/o + # and get prompted it takes 30 seconds to fail + # boot.initrd.systemd.mounts = [ + # { + # what = USB_KEY; + # type = "bcachefs"; + # where = "/usb_key"; + # options = "ro"; + # description = "key"; + # wantedBy = [ + # "initrd.target" + # "initrd-root-fs.target" + # ]; + # } + # ]; boot.initrd.systemd.services.unlock-bcachefs-custom = { description = "Custom single bcachefs unlock for all subvolumes"; @@ -160,125 +141,166 @@ lib.mkMerge [ "sysroot.mount" "initrd-root-fs.target" ]; - # Stronger than wantedBy if you want it absolutely required: - requiredBy = [ - "sysroot.mount" - "initrd-root-fs.target" - ]; before = [ "sysroot.mount" "initrd-root-fs.target" ]; requires = [ - "usb_key.mount" primaryDeviceUnit ]; after = [ - "usb_key.mount" + # "usb_key.mount" primaryDeviceUnit "initrd-root-device.target" ]; - # script = '' - # echo "Using test password..." - # - # mkdir -p /usb_key - # # Wait for USB device (optional, to handle slow init) - # for i in $(seq 1 50); do - # if [ -b "${USB_KEY}" ]; then - # break - # fi - # echo "Waiting for USB key ${USB_KEY}..." - # sleep 0.2 - # done - # - # if [ ! -b "${USB_KEY}" ]; then - # echo "USB key device ${USB_KEY} not present in initrd" - # exit 1 - # fi - # - # # Mount the key - # mount -t bcachefs -o ro "${USB_KEY}" /usb_key - # - # echo "test" | ${pkgs.bcachefs-tools}/bin/bcachefs unlock "${PRIMARY}" - # echo "bcachefs unlock successful for ${PRIMARY}" - # ''; - - script = '' - echo "Using test password..." - echo "test" | ${pkgs.bcachefs-tools}/bin/bcachefs unlock "${PRIMARY}" - echo "bcachefs unlock successful for ${PRIMARY}" - ''; + # unitConfig = { + # # Ensure this service doesn't time out if USB detection takes a while + # DefaultDependencies = "no"; + # }; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + KeyringMode = "shared"; # TODO so it shares with reset root below, not needed otherwise + }; # script = '' # echo "Using USB key for bcachefs unlock: ${USB_KEY}" # - # echo "test" | ${pkgs.bcachefs-tools}/bin/bcachefs unlock "${PRIMARY}" - # echo "done...." - # exit 0 - # # # only try mount if the node exists # if [ ! -e "${USB_KEY}" ]; then # echo "USB key device ${USB_KEY} not present in initrd" # exit 1 # fi + # # ${pkgs.bcachefs-tools}/bin/bcachefs unlock -f /usb_key/key "${PRIMARY}" # echo "bcachefs unlock successful for ${PRIMARY}" # ''; + + script = '' + echo "Searching for USB Unlock Key..." + KEY_FOUND=0 + # 4 second search + for i in {1..40}; do + if [ -e "${USB_KEY}" ]; then + KEY_FOUND=1 + break + fi + sleep 0.1 + done + + if [ "$KEY_FOUND" -eq 1 ]; then + echo "USB Key found at ${USB_KEY}. Attempting unlock..." + mkdir -p /tmp/usb_key_mount + + # Mount read-only + if mount -t bcachefs -o ro "${USB_KEY}" /tmp/usb_key_mount; then + # Attempt unlock + ${pkgs.bcachefs-tools}/bin/bcachefs unlock -f /tmp/usb_key_mount/key "${PRIMARY}" + UNLOCK_STATUS=$? + + # Cleanup + umount /tmp/usb_key_mount + + if [ $UNLOCK_STATUS -eq 0 ]; then + echo "Bcachefs unlock successful!" + exit 0 + else + echo "Failed to unlock with USB key." + fi + else + echo "Failed to mount USB key device." + fi + else + echo "USB Key not found within timeout." + fi + + # 3. Fallback + echo "Proceeding to standard mount (password prompt will appear if still locked)..." + exit 0 + ''; }; # TODO rotate root } # Reset root for erase your darlings/impermanence/preservation - { - # boot.initrd.systemd.services.bcachefs-reset-root = { - # description = "Reset bcachefs root subvolume before pivot"; - # wantedBy = [ "initrd.target" ]; - # - # after = [ - # "initrd-root-device.target" - # "cryptsetup.target" - # "unlock-bcachefs-custom" - # ]; - # requires = [ primaryDeviceUnit ]; - # - # serviceConfig = { - # Type = "oneshot"; - # # initrd has a minimal PATH; set one explicitly - # Environment = "PATH=/bin:/sbin:/usr/bin:/usr/sbin"; - # # If tools are in /usr, this helps ensure it's in the initrd - # # (you may also need environment.systemPackages + boot.initrd.includeDefaultModules) - # ExecStart = pkgs.writeShellScript "bcachefs-reset-root" '' - # set -euo pipefail - # - # PRIMARY=${PRIMARY} - # - # echo "Unlocking bcachefs volume ${PRIMARY}..." - # echo "test" | bcachefs unlock "''${PRIMARY}" - # - # mkdir -p /primary_tmp - # mount "''${PRIMARY}" /primary_tmp - # - # if [[ -e /primary_tmp/@root ]]; then - # mkdir -p /primary_tmp/@old_roots - # bcachefs set-file-option /primary_tmp/@old_roots --compression=zstd - # - # timestamp=$(date --date="@$(stat -c %Y /primary_tmp/@root)" "+%Y-%m-%-d_%H:%M:%S") - # bcachefs subvolume snapshot /primary_tmp/@root "/primary_tmp/@old_roots/$timestamp" - # bcachefs subvolume delete /primary_tmp/@root - # - # # Cleanup old snapshots (>30 days) - # # Note: path was /primary_tmp/old_roots in your snippet; using @old_roots for consistency - # for i in $(find /primary_tmp/@old_roots/ -maxdepth 1 -mtime +30); do - # bcachefs subvolume delete "$i" - # done - # fi - # - # bcachefs subvolume create /primary_tmp/@root - # umount /primary_tmp - # ''; - # }; - # }; - } + (lib.mkIf false { + boot.initrd.systemd.services.bcachefs-reset-root = { + description = "Reset bcachefs root subvolume before pivot"; + + # We want this to run after we've ATTEMPTED to unlock, + # but strictly BEFORE the real root is mounted at /sysroot + after = [ + "initrd-root-device.target" + "cryptsetup.target" + "unlock-bcachefs-custom.service" + ]; + + # This is the most important part: prevent sysroot from mounting until we are done resetting it + before = [ + "sysroot.mount" + ]; + + requires = [ primaryDeviceUnit ]; + wantedBy = [ "initrd.target" ]; + + serviceConfig = { + Type = "oneshot"; + KeyringMode = "shared"; + # We need a path that includes standard tools (mkdir, mount, find, date, stat) + # and bcachefs tools. + Environment = "PATH=${ + lib.makeBinPath [ + pkgs.coreutils + pkgs.util-linux + pkgs.findutils + pkgs.bcachefs-tools + ] + }:/bin:/sbin"; + }; + + script = '' + # 1. Safety check: Try to see if we can read the device. + # If the unlock script failed (or user hasn't typed password yet), this mount will fail. + # We should probably exit non-zero to stop the boot or loop here? + # Actually, if we fail here, the boot continues to sysroot.mount, which will prompt for password, + # BUT we will have skipped the reset. This is a trade-off. + + mkdir -p /primary_tmp + + # Try mounting. If locked, this fails. + if ! mount "${PRIMARY}" /primary_tmp; then + echo "bcachefs-reset-root: Failed to mount ${PRIMARY}. Drive might be locked." + echo "Skipping root reset." + exit 0 + fi + + # 2. Perform the Snapshot & Reset + if [[ -e /primary_tmp/@root ]]; then + mkdir -p /primary_tmp/@snapshots/old_roots + + # Format: YYYY-MM-DD_HH:MM:SS + timestamp=$(date --date="@$(stat -c %Y /primary_tmp/@root)" "+%Y-%m-%-d_%H:%M:%S") + + echo "Snapshotting old root to @snapshots/old_roots/$timestamp" + bcachefs subvolume snapshot /primary_tmp/@root "/primary_tmp/@snapshots/old_roots/$timestamp" + + echo "Deleting current @root" + bcachefs subvolume delete /primary_tmp/@root + + # Cleanup old snapshots (>30 days) + echo "Cleaning up old snapshots..." + find /primary_tmp/@snapshots/old_roots/ -maxdepth 1 -mtime +30 -print0 | xargs -0 -r -I {} sh -c 'echo "Deleting {}"; bcachefs subvolume delete "{}"' + fi + + # 3. Create fresh root + echo "Creating fresh @root subvolume" + bcachefs subvolume create /primary_tmp/@root + + # 4. Cleanup + umount /primary_tmp + ''; + }; + }) ] diff --git a/utilities/nixos-installers/install_bcachefs.md b/utilities/nixos-installers/install_bcachefs.md index e8768572..35b46a4f 100644 --- a/utilities/nixos-installers/install_bcachefs.md +++ b/utilities/nixos-installers/install_bcachefs.md @@ -34,7 +34,6 @@ mkfs.fat -F 32 -n BOOT /dev/$BOOT ```sh PRIMARY=sda2 -# keyctl link @u @s bcachefs format --label=nixos --encrypted /dev/$PRIMARY bcachefs unlock /dev/$PRIMARY ``` @@ -50,8 +49,8 @@ swapon /dev/$SWAP ### Setup subvolumes ```sh -# keyctl link @u @s -U=$(lsblk -o fsType,uuid | grep bcachefs | awk '{print $2}') +keyctl link @u @s +U=$(lsblk -o name,uuid | grep $PRIMARY | awk '{print $2}') echo $U mount /dev/disk/by-uuid/$U /mnt