aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-02-24 07:45:34 -0600
committerCraig Jennings <c@cjennings.net>2026-02-24 07:45:34 -0600
commit58ec870dc06f226c24a94f47c363e784bafaee1e (patch)
treee4c3553cdb41409f5f0c90aea21dd5d99c3f34e8
parentc247536d403c09f74bc254e52f0a9edb2f819949 (diff)
downloadarchangel-58ec870dc06f226c24a94f47c363e784bafaee1e.tar.gz
archangel-58ec870dc06f226c24a94f47c363e784bafaee1e.zip
feat: add ZFS encrypted volume tests (single disk + mirror)
Add automated tests for ZFS native encryption, matching existing Btrfs LUKS test coverage. ZFS encrypted boot requires two passphrase entries (ZFSBootMenu + mkinitcpio zfs hook), both sent via QEMU monitor sendkey with timed delays since ZFSBootMenu renders to VGA, not serial.
-rw-r--r--scripts/test-configs/zfs-encrypt.conf16
-rw-r--r--scripts/test-configs/zfs-mirror-encrypt.conf17
-rwxr-xr-xscripts/test-install.sh87
3 files changed, 110 insertions, 10 deletions
diff --git a/scripts/test-configs/zfs-encrypt.conf b/scripts/test-configs/zfs-encrypt.conf
new file mode 100644
index 0000000..0bc3237
--- /dev/null
+++ b/scripts/test-configs/zfs-encrypt.conf
@@ -0,0 +1,16 @@
+# Test config: ZFS single disk with native encryption
+
+# Enable testing mode for automated ZFS passphrase handling
+TESTING=yes
+
+HOSTNAME=test-zfs-encrypt
+TIMEZONE=UTC
+LOCALE=en_US.UTF-8
+KEYMAP=us
+
+DISKS=/dev/vda
+
+ZFS_PASSPHRASE=testpass
+ROOT_PASSWORD=testpass
+
+ENABLE_SSH=yes
diff --git a/scripts/test-configs/zfs-mirror-encrypt.conf b/scripts/test-configs/zfs-mirror-encrypt.conf
new file mode 100644
index 0000000..0f0efab
--- /dev/null
+++ b/scripts/test-configs/zfs-mirror-encrypt.conf
@@ -0,0 +1,17 @@
+# Test config: ZFS 2-disk mirror with native encryption
+
+# Enable testing mode for automated ZFS passphrase handling
+TESTING=yes
+
+HOSTNAME=test-zfs-mirror-encrypt
+TIMEZONE=UTC
+LOCALE=en_US.UTF-8
+KEYMAP=us
+
+DISKS=/dev/vda,/dev/vdb
+RAID_LEVEL=mirror
+
+ZFS_PASSPHRASE=testpass
+ROOT_PASSWORD=testpass
+
+ENABLE_SSH=yes
diff --git a/scripts/test-install.sh b/scripts/test-install.sh
index affd3a3..dc07d6b 100755
--- a/scripts/test-install.sh
+++ b/scripts/test-install.sh
@@ -173,20 +173,20 @@ start_vm() {
}
# Start VM from installed disk (no ISO)
-# Pass "luks" as $3 to add a QEMU monitor socket (needed for GRUB passphrase entry via sendkey)
+# Pass encrypt mode as $3 ("luks" or "zfs") to add a QEMU monitor socket (needed for passphrase entry via sendkey)
start_vm_from_disk() {
local test_name="$1"
local disk_count="$2"
- local luks_mode="${3:-}"
+ local encrypt_mode="${3:-}"
local disk_args
disk_args=$(get_disk_args "$disk_count" "$test_name")
# Reuse existing OVMF vars (preserves EFI boot entries from install)
local ovmf_vars="$VM_DIR/OVMF_VARS_${test_name}.fd"
- # For LUKS: add monitor socket for sendkey (to type GRUB passphrase)
+ # For encrypted configs: add monitor socket for sendkey (to type passphrase)
local monitor_args=""
- if [[ "$luks_mode" == "luks" ]]; then
+ if [[ -n "$encrypt_mode" ]]; then
local monitor_sock="$VM_DIR/monitor-${test_name}.sock"
rm -f "$monitor_sock"
monitor_args="-monitor unix:$monitor_sock,server,nowait"
@@ -363,6 +363,47 @@ send_luks_passphrase() {
return 0
}
+# Send ZFS passphrase via QEMU monitor sendkey
+# Two passphrase prompts occur (both on VGA framebuffer, not serial):
+# 1. ZFSBootMenu prompts to unlock the pool and show boot environments
+# 2. mkinitcpio's zfs hook prompts again when the selected kernel boots
+# We detect the UEFI firmware log to time the first, then wait for the second.
+send_zfs_passphrase() {
+ local test_name="$1"
+ local passphrase="$2"
+ local monitor_sock="$VM_DIR/monitor-${test_name}.sock"
+
+ step "Waiting for ZFSBootMenu to load..."
+
+ # Wait for UEFI to start loading ZFSBootMenu (this appears in serial via BdsDxe)
+ local waited=0
+ while ! grep -q "starting.*ZFSBootMenu" "$SERIAL_LOG" 2>/dev/null; do
+ sleep 1
+ waited=$((waited + 1))
+ if [[ $waited -ge 30 ]]; then
+ error "Timed out waiting for ZFSBootMenu to load"
+ return 1
+ fi
+ done
+ info "ZFSBootMenu loading detected after ${waited}s"
+
+ # Prompt 1: ZFSBootMenu passphrase (unlocks pool to show boot menu)
+ step "Waiting for ZFSBootMenu passphrase prompt (15s)..."
+ sleep 15
+ step "Sending ZFS passphrase (1/2: ZFSBootMenu)..."
+ monitor_sendkeys "$monitor_sock" "$passphrase"
+ info "ZFSBootMenu passphrase sent"
+
+ # Prompt 2: mkinitcpio zfs hook passphrase (re-imports pool during kernel boot)
+ step "Waiting for initramfs passphrase prompt (30s)..."
+ sleep 30
+ step "Sending ZFS passphrase (2/2: initramfs)..."
+ monitor_sendkeys "$monitor_sock" "$passphrase"
+ info "Initramfs passphrase sent"
+
+ return 0
+}
+
# Run SSH command (uses SSH_PASSWORD by default, or INSTALLED_PASSWORD if set)
ssh_cmd() {
local password="${INSTALLED_PASSWORD:-$SSH_PASSWORD}"
@@ -424,6 +465,18 @@ verify_install() {
else
warn "ZFS genesis snapshot not found"
fi
+
+ # Check ZFS native encryption if configured
+ local zfs_pass
+ zfs_pass=$(grep '^ZFS_PASSPHRASE=' "$config" | cut -d= -f2)
+ if [[ -n "$zfs_pass" ]]; then
+ if ssh_cmd "zfs get -H -o value encryption zroot/ROOT" | grep -q "aes-256-gcm"; then
+ info "ZFS encryption (aes-256-gcm) verified"
+ else
+ error "ZFS encryption not set on zroot/ROOT"
+ return 1
+ fi
+ fi
elif [[ "$filesystem" == "btrfs" ]]; then
# Btrfs-specific checks
if ssh_cmd "btrfs subvolume list /mnt" >/dev/null 2>&1; then
@@ -672,18 +725,22 @@ run_test() {
step "Booting from installed disk..."
: > "$SERIAL_LOG" # Clear serial log
- # Determine if this is a LUKS config (needs monitor for GRUB passphrase)
+ # Determine encryption mode (needs monitor socket for passphrase entry via sendkey)
local luks_passphrase
luks_passphrase=$(grep '^LUKS_PASSPHRASE=' "$config" | cut -d= -f2)
+ local zfs_passphrase
+ zfs_passphrase=$(grep '^ZFS_PASSPHRASE=' "$config" | cut -d= -f2)
local no_encrypt
no_encrypt=$(grep '^NO_ENCRYPT=' "$config" | cut -d= -f2)
- local luks_flag=""
+ local encrypt_flag=""
if [[ -n "$luks_passphrase" && "$no_encrypt" != "yes" ]]; then
- luks_flag="luks"
+ encrypt_flag="luks"
+ elif [[ -n "$zfs_passphrase" && "$no_encrypt" != "yes" ]]; then
+ encrypt_flag="zfs"
fi
local vm_pid2
- vm_pid2=$(start_vm_from_disk "$config_name" "$disk_count" "$luks_flag")
+ vm_pid2=$(start_vm_from_disk "$config_name" "$disk_count" "$encrypt_flag")
if [[ -z "$vm_pid2" ]]; then
error "Failed to start VM from disk"
@@ -694,8 +751,8 @@ run_test() {
fi
info "VM started from disk (PID: $vm_pid2)"
- # If LUKS is enabled, send GRUB passphrase via monitor sendkey
- if [[ -n "$luks_flag" ]]; then
+ # If encryption is enabled, send passphrase via monitor sendkey
+ if [[ "$encrypt_flag" == "luks" ]]; then
if ! send_luks_passphrase "$config_name" "$luks_passphrase" "$disk_count"; then
error "Failed to send LUKS passphrase"
stop_vm "$config_name"
@@ -705,6 +762,16 @@ run_test() {
FAILED_TESTS+=("$config_name")
return 1
fi
+ elif [[ "$encrypt_flag" == "zfs" ]]; then
+ if ! send_zfs_passphrase "$config_name" "$zfs_passphrase"; then
+ error "Failed to send ZFS passphrase"
+ stop_vm "$config_name"
+ cp "$SERIAL_LOG" "$LOG_DIR/${config_name}-reboot-serial.log" 2>/dev/null || true
+ cleanup_disks "$config_name"
+ TESTS_FAILED=$((TESTS_FAILED + 1))
+ FAILED_TESTS+=("$config_name")
+ return 1
+ fi
fi
# Check if SSH is enabled in the config