aboutsummaryrefslogtreecommitdiff
path: root/installer
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-08-02 23:12:39 -0500
committerCraig Jennings <c@cjennings.net>2026-08-02 23:12:39 -0500
commit72dc6c49636aefd12f707ffc3eeb244744133cae (patch)
treedf9e816ef3109536d4c2b57cbb630040834c4479 /installer
parent1da64394f637347414ffc4954daf9c72d180e2d0 (diff)
downloadarchangel-72dc6c49636aefd12f707ffc3eeb244744133cae.tar.gz
archangel-72dc6c49636aefd12f707ffc3eeb244744133cae.zip
fix(install): stop encrypted ZFS boots asking for the passphrase twiceHEADmain
ZFSBootMenu unlocks the pool to read the kernel and initramfs, then kexecs into it. The loaded key doesn't survive kexec. The booted initramfs re-imports the pool, finds keylocation=prompt, and asks for the same passphrase again. I write the passphrase to /etc/zfs/zroot.key inside the encrypted root, point the encryption root at it, and bake it into the initramfs. ZFSBootMenu can't read a file in a dataset it hasn't unlocked, so it overrides the file:// URI and prompts once. The booted initramfs then loads the key silently. Nothing weakens at rest. Both the keyfile and the initramfs live inside the encrypted dataset, which only holds because ZFSBootMenu keeps the initramfs inside the boot environment rather than on the ESP. keyformat stays passphrase, since that's what lets ZFSBootMenu accept the typed value. keylocation alone is settable with zfs set, so this never reaches for zfs change-key and never rekeys the pool mid-install. The Btrfs path already did this for LUKS, so I reused its ensure_initramfs_files helper.
Diffstat (limited to 'installer')
-rwxr-xr-xinstaller/archangel47
1 files changed, 47 insertions, 0 deletions
diff --git a/installer/archangel b/installer/archangel
index 4805796..b9817e8 100755
--- a/installer/archangel
+++ b/installer/archangel
@@ -925,6 +925,47 @@ configure_ssh() {
fi
}
+# Close the second passphrase prompt on encrypted ZFS boots.
+#
+# ZFSBootMenu unlocks the pool to read this kernel and initramfs, then kexecs
+# into it — and the loaded key does not survive kexec. With no key inside the
+# initramfs, the zfs hook re-imports the pool, finds keylocation=prompt, and
+# asks for the same passphrase a second time.
+#
+# Pointing the encryption root at a keyfile that lives inside the encrypted
+# dataset closes it. ZFSBootMenu cannot read a file in a dataset it has not
+# unlocked, so it overrides the file:// URI and prompts once — documented
+# upstream behavior, not a side effect. The booted initramfs carries the
+# keyfile and loads the key silently.
+#
+# keyformat stays passphrase: it is what lets ZFSBootMenu accept the typed
+# value, and a raw key would leave it no way in at all. keylocation alone is
+# settable with `zfs set` (zfsprops(7)), so this never reaches for `zfs
+# change-key`, which would rekey the pool and prompt for new key material
+# mid-install.
+#
+# Never relocate this keyfile onto the ESP or into a custom ZFSBootMenu image.
+# Both are unencrypted; the protection here comes entirely from the keyfile and
+# the initramfs living inside the encrypted dataset.
+configure_zfs_keyfile() {
+ local passphrase="$1"
+ local pool="$2"
+ local keyfile="/etc/zfs/zroot.key"
+
+ mkdir -p "$MNTPOINT$(dirname "$keyfile")"
+
+ # No trailing newline: ZFS reads the file's bytes as the passphrase, so a
+ # stray newline would not match what the user types at the ZBM prompt.
+ printf '%s' "$passphrase" > "$MNTPOINT$keyfile"
+ chmod 000 "$MNTPOINT$keyfile"
+
+ zfs set keylocation="file://$keyfile" "$pool" \
+ || error "Failed to point $pool at $keyfile"
+
+ ensure_initramfs_files "$keyfile" "$MNTPOINT/etc/mkinitcpio.conf"
+ info "Keyfile embedded in initramfs - one passphrase prompt at boot."
+}
+
configure_initramfs() {
step "Configuring Initramfs for ZFS"
@@ -984,6 +1025,12 @@ EOF
# system. (Audited 2026-04-27 against silent-sed pattern.)
sed -i 's/^HOOKS=.*/HOOKS=(base udev microcode modconf kms keyboard keymap consolefont block zfs filesystems)/' $MNTPOINT/etc/mkinitcpio.conf
+ # Embed the pool key so the booted initramfs doesn't re-prompt. Must run
+ # before mkinitcpio -P below, which is what bakes FILES= into the image.
+ if [[ "$NO_ENCRYPT" != "yes" ]]; then
+ configure_zfs_keyfile "$ZFS_PASSPHRASE" "$POOL_NAME"
+ fi
+
# Get the installed kernel version (not the running kernel)
local kernel_ver
kernel_ver=$(ls $MNTPOINT/usr/lib/modules | grep lts | head -1)