From 0f4c5e185dc0df0c486ec5ce768334c4ff38cde1 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Mon, 23 Feb 2026 10:31:08 -0600 Subject: feat: add LUKS passphrase automation to VM test framework - Add monitor_sendkeys() to type strings into QEMU via monitor socket - Add send_luks_passphrase() that detects GRUB passphrase prompt in serial log and sends passphrase via sendkey, supporting multi-disk LUKS (one passphrase per encrypted disk) - Add QEMU monitor socket to start_vm_from_disk() for LUKS configs - Auto-detect LUKS configs and handle passphrase entry during reboot test - Add socat dependency check --- scripts/test-install.sh | 118 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/test-install.sh b/scripts/test-install.sh index 5bf7a6e..3004ae3 100755 --- a/scripts/test-install.sh +++ b/scripts/test-install.sh @@ -173,15 +173,25 @@ 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) start_vm_from_disk() { local test_name="$1" local disk_count="$2" + local luks_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) + local monitor_args="" + if [[ "$luks_mode" == "luks" ]]; then + local monitor_sock="$VM_DIR/monitor-${test_name}.sock" + rm -f "$monitor_sock" + monitor_args="-monitor unix:$monitor_sock,server,nowait" + fi + # Start VM without ISO, boot from disk qemu-system-x86_64 \ -name "archangel-test-$test_name" \ @@ -196,6 +206,7 @@ start_vm_from_disk() { -netdev user,id=net0,hostfwd=tcp::"$SSH_PORT"-:22 \ -device virtio-net-pci,netdev=net0 \ -serial file:"$SERIAL_LOG" \ + $monitor_args \ -display none \ -daemonize \ -pidfile "$VM_DIR/qemu-${test_name}.pid" \ @@ -226,6 +237,9 @@ stop_vm() { if [[ "$keep_vars" != "true" ]]; then rm -f "$VM_DIR/OVMF_VARS_${test_name}.fd" fi + + # Clean up monitor socket + rm -f "$VM_DIR/monitor-${test_name}.sock" } # Wait for SSH to be available @@ -272,6 +286,83 @@ wait_for_boot_console() { done } +# Send a string as QEMU monitor sendkey commands +# Converts each character to a QEMU key name and sends via monitor socket +monitor_sendkeys() { + local monitor_sock="$1" + local text="$2" + + for ((ci=0; ci<${#text}; ci++)); do + local char="${text:$ci:1}" + local key="" + case "$char" in + [a-z]) key="$char" ;; + [A-Z]) key="shift-$(echo "$char" | tr '[:upper:]' '[:lower:]')" ;; + [0-9]) key="$char" ;; + ' ') key="spc" ;; + '-') key="minus" ;; + '=') key="equal" ;; + '.') key="dot" ;; + ',') key="comma" ;; + '/') key="slash" ;; + '\\') key="backslash" ;; + ';') key="semicolon" ;; + "'") key="apostrophe" ;; + '[') key="bracket_left" ;; + ']') key="bracket_right" ;; + '!') key="shift-1" ;; + '@') key="shift-2" ;; + '#') key="shift-3" ;; + '$') key="shift-4" ;; + *) key="$char" ;; + esac + echo "sendkey $key" | socat - UNIX-CONNECT:"$monitor_sock" >/dev/null 2>&1 + sleep 0.05 # Small delay between keystrokes + done + # Send Enter + echo "sendkey ret" | socat - UNIX-CONNECT:"$monitor_sock" >/dev/null 2>&1 +} + +# Send GRUB passphrase via QEMU monitor sendkey +# The initramfs passphrase is handled by the cryptkey=rootfs: parameter +# For multi-disk LUKS (mirror), GRUB prompts for each disk separately +send_luks_passphrase() { + local test_name="$1" + local passphrase="$2" + local disk_count="${3:-1}" + local monitor_sock="$VM_DIR/monitor-${test_name}.sock" + + step "Waiting for GRUB passphrase prompt..." + + # Wait for first GRUB passphrase prompt + local waited=0 + while ! grep -q "Enter passphrase for" "$SERIAL_LOG" 2>/dev/null; do + sleep 1 + waited=$((waited + 1)) + if [[ $waited -ge 30 ]]; then + error "Timed out waiting for GRUB passphrase prompt" + return 1 + fi + done + info "GRUB passphrase prompt detected after ${waited}s" + + # Send passphrase for each LUKS disk (GRUB prompts once per encrypted disk) + for ((i=1; i<=disk_count; i++)); do + sleep 1 # Ensure GRUB is ready for input + step "Sending GRUB passphrase ($i/$disk_count) via monitor sendkey..." + monitor_sendkeys "$monitor_sock" "$passphrase" + + # Wait between passphrases for GRUB to decrypt and show next prompt + if [[ $i -lt $disk_count ]]; then + info "Waiting for next GRUB passphrase prompt..." + sleep 8 + fi + done + + info "All GRUB passphrases sent" + return 0 +} + # Run SSH command (uses SSH_PASSWORD by default, or INSTALLED_PASSWORD if set) ssh_cmd() { local password="${INSTALLED_PASSWORD:-$SSH_PASSWORD}" @@ -580,8 +671,19 @@ run_test() { # Boot from installed disk (no ISO) step "Booting from installed disk..." : > "$SERIAL_LOG" # Clear serial log + + # Determine if this is a LUKS config (needs monitor for GRUB passphrase) + local luks_passphrase + luks_passphrase=$(grep '^LUKS_PASSPHRASE=' "$config" | cut -d= -f2) + local no_encrypt + no_encrypt=$(grep '^NO_ENCRYPT=' "$config" | cut -d= -f2) + local luks_flag="" + if [[ -n "$luks_passphrase" && "$no_encrypt" != "yes" ]]; then + luks_flag="luks" + fi + local vm_pid2 - vm_pid2=$(start_vm_from_disk "$config_name" "$disk_count") + vm_pid2=$(start_vm_from_disk "$config_name" "$disk_count" "$luks_flag") if [[ -z "$vm_pid2" ]]; then error "Failed to start VM from disk" @@ -592,6 +694,19 @@ 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 ! send_luks_passphrase "$config_name" "$luks_passphrase" "$disk_count"; then + error "Failed to send LUKS 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 local enable_ssh enable_ssh=$(grep '^ENABLE_SSH=' "$config" | cut -d= -f2) @@ -687,6 +802,7 @@ main() { # Check dependencies command -v qemu-system-x86_64 >/dev/null 2>&1 || { error "qemu not found"; exit 1; } command -v sshpass >/dev/null 2>&1 || { error "sshpass not found"; exit 1; } + command -v socat >/dev/null 2>&1 || { error "socat not found (needed for LUKS tests)"; exit 1; } [[ -f "$OVMF_CODE" ]] || { error "OVMF not found: $OVMF_CODE"; exit 1; } # Find ISO -- cgit v1.2.3