From 0b131a3cc0f5f4014d70ada120aae82330365b12 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Tue, 17 Dec 2024 15:11:59 +0200 Subject: [PATCH 1/9] tests: add spread test for hybrid passphrase support This spread test is disabled currently since the corrosponding passphrase support didn't land yet. Signed-off-by: Zeyad Gouda --- .../developer1-2410-classic-dangerous.json | 42 ++++ .../remote/remote.wait-for | 41 +++- tests/lib/nested.sh | 13 ++ tests/lib/tools/setup_nested_hybrid_system.sh | 33 ++- tests/lib/tools/tests.nested | 10 +- .../passphrase-support-on-hybrid/task.yaml | 198 ++++++++++++++++++ 6 files changed, 323 insertions(+), 14 deletions(-) create mode 100644 tests/lib/assertions/developer1-2410-classic-dangerous.json create mode 100644 tests/nested/manual/passphrase-support-on-hybrid/task.yaml diff --git a/tests/lib/assertions/developer1-2410-classic-dangerous.json b/tests/lib/assertions/developer1-2410-classic-dangerous.json new file mode 100644 index 00000000000..b874c21246e --- /dev/null +++ b/tests/lib/assertions/developer1-2410-classic-dangerous.json @@ -0,0 +1,42 @@ +{ + "type": "model", + "authority-id": "developer1", + "series": "16", + "brand-id": "developer1", + "model": "developer1-2410-classic-dangerous", + "architecture": "amd64", + "timestamp": "2024-04-09T22:00:00+00:00", + "grade": "dangerous", + "base": "core22", + "classic": "true", + "distribution": "ubuntu", + "serial-authority": [ + "generic" + ], + "snaps": [ + { + "default-channel": "classic-24.10/edge", + "id": "UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH", + "name": "pc", + "type": "gadget" + }, + { + "default-channel": "24.10/edge", + "id": "pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza", + "name": "pc-kernel", + "type": "kernel" + }, + { + "default-channel": "latest/edge", + "id": "amcUKQILKXHHTlmSa7NMdnXSx02dNeeT", + "name": "core22", + "type": "base" + }, + { + "default-channel": "latest/stable", + "id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4", + "name": "snapd", + "type": "snapd" + } + ] +} diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for index a16179d8b1b..0860290462f 100755 --- a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for +++ b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for @@ -17,7 +17,7 @@ show_help() { echo "usage: remote.wait-for ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for no-ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for snap-command [--wait WAIT] [-n|--attempts ATTEMPTS]" - echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS]" + echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS] [--expect-passphrase PASSPHRASE] [--log-file PATH]" echo " remote.wait-for device-initialized [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for refresh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo "" @@ -99,7 +99,9 @@ wait_for_reconnect_ssh() { wait_for_reboot() { local attempts=${1:-$DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS} local wait=${2:-$DEFAULT_WAIT_FOR_REBOOT_WAIT} - local initial_boot_id=$3 + local expect_passphrase=${3:-} + local log_file=${4:-} + local initial_boot_id=$5 local last_boot_id echo "remote.wait-for: waiting for reboot" @@ -110,6 +112,24 @@ wait_for_reboot() { return fi + if [ -n "$expect_passphrase" ]; then + if [ ! -f "$log_file" ]; then + echo "remote.wait-for --expect-passphrase without passing an existing --log-file" + return 1 + fi + while [ "$attempts" -ge 0 ]; do + echo -n '.' + attempts=$(( attempts - 1 )) + # Wait for passphrase prompt from serial log file. + if MATCH "Please enter the passphrase" < ${log_file}; then + # Enter passphrase to continue boot. + echo "$expect_passphrase" | netcat -N localhost 7777 + break + fi + sleep "$wait" + done + fi + while [ "$attempts" -ge 0 ]; do echo -n '.' attempts=$(( attempts - 1 )) @@ -123,7 +143,10 @@ wait_for_reboot() { done echo "" - if [ "$last_boot_id" != "$initial_boot_id" ]; then + if [ -z "$last_boot_id" ]; then + echo "remote.wait-for: could not get last boot id" + return 1 + elif [ "$last_boot_id" != "$initial_boot_id" ]; then echo "remote.wait-for: reboot completed" else echo "remote.wait-for: boot id did not change" @@ -241,7 +264,7 @@ main() { exit fi - local action wait attempts others + local action wait attempts expect_passphrase log_file others case "$1" in -h|--help) show_help @@ -293,6 +316,14 @@ main() { attempts=$2 shift 2 ;; + --expect-passphrase) + expect_passphrase="$2" + shift 2 + ;; + --log-file) + log_file="$2" + shift 2 + ;; *) if [ -z "$others" ]; then others=$1 @@ -304,7 +335,7 @@ main() { esac done - "$action" "$attempts" "$wait" "$others" + "$action" "$attempts" "$wait" "$expect_passphrase" "$log_file" "$others" } main "$@" diff --git a/tests/lib/nested.sh b/tests/lib/nested.sh index f13e55efa36..1e2c9ba1ab9 100755 --- a/tests/lib/nested.sh +++ b/tests/lib/nested.sh @@ -1204,6 +1204,9 @@ nested_start_core_vm_unit() { PARAM_RTC="${NESTED_PARAM_RTC:-}" PARAM_EXTRA="${NESTED_PARAM_EXTRA:-}" + local EXPECT_PASSPHRASE + EXPECT_PASSPHRASE=${NESTED_EXPECT_PASSPHRASE:-} + # Open port 7777 on the host so that failures in the nested VM (e.g. to # create users) can be debugged interactively via # "telnet localhost 7777". Also keeps the logs @@ -1211,6 +1214,9 @@ nested_start_core_vm_unit() { # XXX: should serial just be logged to stdout so that we just need # to "journalctl -u $NESTED_VM" to see what is going on ? if "$QEMU" -version | grep '2\.5'; then + if [ -z "$EXPECT_PASSPHRASE" ]; then + echo "internal error: NESTED_EXPECT_PASSPHRASE is set and qemu doesn't support chardev over socket" + fi # XXX: remove once we no longer support xenial hosts PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log" else @@ -1371,6 +1377,13 @@ nested_start_core_vm_unit() { ${PARAM_CD} \ ${PARAM_EXTRA} " "${PARAM_REEXEC_ON_FAILURE}" + if [ -n "$EXPECT_PASSPHRASE" ]; then + # Wait for passphrase prompt from serial log file. + retry -n 120 --wait 1 sh -c "MATCH \"Please enter the passphrase\" < ${NESTED_LOGS_DIR}/serial.log" + # Enter passphrase to continue boot. + echo "$EXPECT_PASSPHRASE" | netcat -N localhost 7777 + fi + local EXPECT_SHUTDOWN EXPECT_SHUTDOWN=${NESTED_EXPECT_SHUTDOWN:-} diff --git a/tests/lib/tools/setup_nested_hybrid_system.sh b/tests/lib/tools/setup_nested_hybrid_system.sh index fd867c0e95d..e3352c367dd 100755 --- a/tests/lib/tools/setup_nested_hybrid_system.sh +++ b/tests/lib/tools/setup_nested_hybrid_system.sh @@ -20,6 +20,8 @@ run_muinstaller() { local label="${7}" local disk="${8}" local kern_mods_comp="${9:-}" + local passphrase="${10}" + local extra_muinstaller_args="${11}" # ack the needed assertions snap ack "${kernel_assertion}" @@ -164,10 +166,15 @@ fi EOF remote.exec "chmod +x /home/user1/mk-classic-rootfs-wrapper.sh" - remote.exec "sudo muinstaller \ - -label ${label} \ - -device ${install_disk} \ - -rootfs-creator /home/user1/mk-classic-rootfs-wrapper.sh" + muinstaller_args=() + muinstaller_args+=("-label $label") + muinstaller_args+=("-device $install_disk") + muinstaller_args+=("-rootfs-creator /home/user1/mk-classic-rootfs-wrapper.sh") + if [ -n "$passphrase" ]; then + muinstaller_args+=("-passphrase \"$passphrase\"") + fi + muinstaller_args+=("${extra_muinstaller_args[@]}") + remote.exec sudo muinstaller "${muinstaller_args[@]}" remote.exec "sudo sync" @@ -196,7 +203,11 @@ EOF fi # Start installed image - tests.nested create-vm core --keep-firmware-state + if [ -n "$passphrase" ]; then + tests.nested create-vm core --keep-firmware-state --expect-passphrase "$passphrase" + else + tests.nested create-vm core --keep-firmware-state + fi } main() { @@ -209,6 +220,8 @@ main() { local label="classic" local disk="" local kern_mods_comp="" + local passphrase="" + local extra_muinstaller_args="" while [ $# -gt 0 ]; do case "$1" in --model) @@ -248,6 +261,14 @@ main() { kern_mods_comp="${2}" shift 2 ;; + --passphrase) + passphrase="${2}" + shift 2 + ;; + --extra-muinstaller-args) + extra_muinstaller_args="${2}" + shift 2 + ;; --*|-*) echo "Unknown option ${1}" exit 1 @@ -338,7 +359,7 @@ main() { run_muinstaller "${model_assertion}" "${store_dir}" "${gadget_snap}" \ "${gadget_assertion}" "${kernel_snap}" "${kernel_assertion}" "${label}" \ - "${disk}" "${kern_mods_comp}" + "${disk}" "${kern_mods_comp}" "${passphrase}" "${extra_muinstaller_args}" ) } diff --git a/tests/lib/tools/tests.nested b/tests/lib/tools/tests.nested index 1175ff17d2a..535ecd42f13 100755 --- a/tests/lib/tools/tests.nested +++ b/tests/lib/tools/tests.nested @@ -133,7 +133,7 @@ create_vm() { ;; esac - local NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE + local NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE while [ $# -gt 0 ]; do case "$1" in --param-cdrom) @@ -156,6 +156,10 @@ create_vm() { NESTED_KEEP_FIRMWARE_STATE=1 shift ;; + --expect-passphrase) + NESTED_EXPECT_PASSPHRASE="$2" + shift 2 + ;; *) echo "tests.nested: unsupported parameter $1" >&2 exit 1 @@ -163,9 +167,9 @@ create_vm() { esac done - export NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE + export NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE "$action" - unset NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE + unset NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE } is_nested() { diff --git a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml new file mode 100644 index 00000000000..b71c54c730e --- /dev/null +++ b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml @@ -0,0 +1,198 @@ +summary: End-to-end test for FDE passphrase support on hybrid systems + +details: | + This test installs an encrypted hybrid Ubuntu system using muinstaller + which is protected by passphrase authentication. + +systems: [ubuntu-24.04-64] + +# TODO: Remove this when passphrase support lands. +manual: true + +environment: + NESTED_ENABLE_TPM: true + NESTED_ENABLE_SECURE_BOOT: true + + # TODO: Swap 24.10 with 25.04 when the kernel/gadget snaps are available in the store. + # Mimic default channels specified in https://github.com/canonical/models/blob/master/ubuntu-classic-2504-amd64-dangerous.json. + GADGET_CHANNEL: classic-24.10/edge + KERNEL_CHANNEL: 24.10/edge + CORE_VERSION: 22 + + PASSPHRASE: ubuntu + # "pbkdf2" is less memory intensive than argon2i* so we use it by default. + # TODO: Add argon2i* variants. + KDF_TYPE: pbkdf2 + + # Ensure we use our latest code. + NESTED_BUILD_SNAPD_FROM_CURRENT: true + NESTED_REPACK_KERNEL_SNAP: true + NESTED_ENABLE_OVMF: true + # Store related setup. + STORE_ADDR: localhost:11028 + STORE_DIR: $(pwd)/fake-store-blobdir + +prepare: | + if [ "$TRUST_TEST_KEYS" = "false" ]; then + echo "This test needs test keys to be trusted" + exit + fi + apt install dosfstools kpartx + snap install jq + snap install yq + + # Fakestore is needed for "snap prepare-image". + "$TESTSTOOLS"/store-state setup-fake-store "$STORE_DIR" + +restore: | + "$TESTSTOOLS"/store-state teardown-fake-store "$STORE_DIR" + rm -rf ./classic-root + +execute: | + # shellcheck source=tests/lib/prepare.sh + . "$TESTSLIB/prepare.sh" + #shellcheck source=tests/lib/nested.sh + . "$TESTSLIB"/nested.sh + + # Expose the needed assertions through the fakestore. + cp "$TESTSLIB"/assertions/developer1.account "$STORE_DIR/asserts" + cp "$TESTSLIB"/assertions/developer1.account-key "$STORE_DIR/asserts" + cp "$TESTSLIB"/assertions/testrootorg-store.account-key "$STORE_DIR/asserts" + export SNAPPY_FORCE_SAS_URL=http://$STORE_ADDR + + # Retrieve the gadget. + snap download --basename=pc --channel="$GADGET_CHANNEL" pc + + # Modify gadget and resign with snakeoil keys. + unsquashfs -d pc-gadget pc.snap + echo 'console=ttyS0 systemd.journald.forward_to_console=1' > pc-gadget/cmdline.extra + echo "Sign the shim binary" + KEY_NAME=$(tests.nested download snakeoil-key) + SNAKEOIL_KEY="$PWD/$KEY_NAME.key" + SNAKEOIL_CERT="$PWD/$KEY_NAME.pem" + tests.nested secboot-sign gadget pc-gadget "$SNAKEOIL_KEY" "$SNAKEOIL_CERT" + snap pack --filename=pc.snap pc-gadget/ + + # Retrieve kernel. + snap download --basename=pc-kernel --channel="$KERNEL_CHANNEL" pc-kernel + # Build kernel with initramfs with the compiled snap-bootstrap + uc24_build_initramfs_kernel_snap "$PWD/pc-kernel.snap" "$NESTED_ASSETS_DIR" + mv "${NESTED_ASSETS_DIR}"/pc-kernel_*.snap pc-kernel.snap + + # Create new disk for the installer to work on and attach to VM. + truncate --size=6G disk.img + + # setup_nested_hybrid_system.sh runs the muinstaller to install a hybrid + # system. + gendeveloper1 sign-model < "$TESTSLIB"/assertions/developer1-2410-classic-dangerous.json > classic.model + "${TESTSTOOLS}"/setup_nested_hybrid_system.sh \ + --model classic.model \ + --store-dir "${STORE_DIR}" \ + --gadget pc.snap \ + --gadget-assertion pc.assert \ + --kernel pc-kernel.snap \ + --kernel-assertion pc-kernel.assert \ + --passphrase "$PASSPHRASE" \ + --extra-muinstaller-args "-kdf-type $KDF_TYPE" \ + --disk disk.img + + # Basic things look fine. + remote.exec "cat /etc/os-release" | MATCH 'NAME="Ubuntu"' + remote.exec "snap changes" | MATCH "Done.* Initialize system state" + remote.exec "snap list" | MATCH pc-kernel + + # Check encryption. + remote.exec "sudo test -d /var/lib/snapd/device/fde" + remote.exec "sudo test -e /var/lib/snapd/device/fde/marker" + remote.exec "sudo test -e /var/lib/snapd/device/fde/marker" + remote.exec "sudo blkid /dev/disk/by-label/ubuntu-data-enc" | MATCH crypto_LUKS + + # Check disk mappings. + remote.exec "sudo snap install jq" + # TODO: no ubuntu-save right now because: + # "ERROR cannot store device key pair: internal error: cannot access device keypair manager if ubuntu-save is unavailable" + #DISK_MAPPINGS=(/run/mnt/ubuntu-save/device/disk-mapping.json + # /run/mnt/data/var/lib/snapd/device/disk-mapping.json) + DISK_MAPPINGS=(/run/mnt/data/var/lib/snapd/device/disk-mapping.json) + for DM in "${DISK_MAPPINGS[@]}"; do + remote.exec "sudo cat $DM | jq '.pc.\"structure-encryption\".\"ubuntu-save\".method'" | MATCH '"LUKS"' + remote.exec "sudo cat $DM | jq '.pc.\"structure-encryption\".\"ubuntu-data\".method'" | MATCH '"LUKS"' + done + + # Check that on an already provisioned system the API will give a + # sensible reason why the system cannot be installed without further + # action. + remote.exec "sudo snap debug api /v2/systems/classic" > system + jq '.result."storage-encryption".support' < system | MATCH "unavailable" + jq '.result."storage-encryption"."unavailable-reason"' < system | MATCH "not encrypting device storage as checking TPM gave: the TPM is in DA lockout mode" + + # refresh rebooting snap + # $1: path to snap file + # $2: snap name + # $3: reboot action ("reboot"/"no-reboot") + refresh_rebooting_snap() + { + local snap_filename=$1 + local snap_name=$2 + local reboot_action=$3 + + boot_id=$(tests.nested boot-id) + + printf "Test installing snap from file %s\n" "$snap_filename" + remote.push "$snap_filename" + # install will exit when waiting for the reboot + remote.exec sudo snap install --dangerous "$snap_filename" | MATCH "Task set to wait until a system restart allows to continue" + + # Check that a reboot notification was setup. + remote.exec test -f /run/reboot-required + remote.exec cat /run/reboot-required.pkgs | MATCH "snap:${snap_name}" + # Check that no reboot has been scheduled, then force a reboot + remote.exec not test -f /run/systemd/shutdown/scheduled + + if [ "$reboot_action" = "reboot" ]; then + local log_file="$NESTED_LOGS_DIR"/serial.log + # Clear old log file to avoid matching passphrase prompt from previous boot. + echo "" > "$log_file" + remote.exec sudo reboot || true + remote.wait-for reboot --wait 1 -n 200 --expect-passphrase "ubuntu" --log-file "$log_file" "$boot_id" + remote.exec sudo snap watch --last=install + fi + } + # Ensure update-notifier-common is installed so that reboot notification works. + remote.exec "sudo apt install -y update-notifier-common" + + # Initial reseal count. + remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains-before.json + reseal_count_start="$(jq -r '.["reseal-count"]' < boot-chains-before.json )" + + fetch_and_check_reseal_count_equal() { + local reseal_count_now + remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains.json + reseal_count_now="$(jq -r '.["reseal-count"]' < boot-chains.json )" + test "$reseal_count_now" = "$1" + } + + # 1. Test gadget refresh causing reseal. + + # Changing cmdline should force a reseal. + echo 'console=ttyS0 systemd.journald.forward_to_console=1 loglevel=4' > pc-gadget/cmdline.extra + tests.nested secboot-sign gadget pc-gadget "$SNAKEOIL_KEY" "$SNAKEOIL_CERT" + snap pack --filename=pc-new.snap pc-gadget/ + refresh_rebooting_snap pc-new.snap pc reboot + + # We expect two reseals, one before the update and one after. + fetch_and_check_reseal_count_equal "$((reseal_count_start + 2))" + + # 2. Test kernel refresh causing reseal. + + # Resigning kernel should be enough to trigger a reseal. + uc24_build_initramfs_kernel_snap "$PWD/pc-kernel.snap" "$PWD/pc-kernel-new.snap" + refresh_rebooting_snap pc-kernel-new.snap pc reboot + + # We expect two reseals, one before the update and one after. + fetch_and_check_reseal_count_equal "$((reseal_count_start + 4))" + + # TODO: 3. Try refreshing to an unsupported kernel when snapd-info files + # are available. + + # TODO: 4. Remodelling? From df1a7b4fea42c2cb29656fb20d46fff328716415 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Tue, 17 Dec 2024 15:13:00 +0200 Subject: [PATCH 2/9] tests/nested/manual/muinstaller-core: add passphrase smoke test variant Signed-off-by: Zeyad Gouda --- .../nested/manual/muinstaller-core/task.yaml | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/nested/manual/muinstaller-core/task.yaml b/tests/nested/manual/muinstaller-core/task.yaml index e8444c541c3..94fd545ed33 100644 --- a/tests/nested/manual/muinstaller-core/task.yaml +++ b/tests/nested/manual/muinstaller-core/task.yaml @@ -14,6 +14,11 @@ environment: NESTED_ENABLE_TPM: true NESTED_ENABLE_SECURE_BOOT: true + # encrypted + passphrase auth smoke test + # TODO: extract a more comprehensive core24 test similar + # to tests/nested/manual/passphrase-support-on-hybrid. + PASSPHRASE/passphrase_auth: "ubuntu" + # unencrypted case NESTED_ENABLE_TPM/plain: false NESTED_ENABLE_SECURE_BOOT/plain: false @@ -58,6 +63,16 @@ execute: | #shellcheck source=tests/lib/nested.sh . "$TESTSLIB"/nested.sh + # TODO: remove this condition when passphrase support lands. + if [ -n "$PASSPHRASE" ]; then + echo "SKIPPING: passphrase support has not landed yet" + exit 0 + fi + # if ! os.query is-noble && [ -n "$PASSPHRASE" ]; then + # echo "SKIPPING: only run passphrase support variant for core24" + # exit 0 + # fi + version="$(nested_get_version)" # Retrieve the gadget @@ -107,6 +122,9 @@ execute: | snap pack ./snap-with-comps --filename=snap-with-comps.snap snap pack ./comp1 --filename=snap-with-comps+comp1.comp + # TODO: refactor preparation/hacks in a reusable script + # similar to setup_nested_hybrid_system.sh + # prepare a core seed SEED_DIR="core-seed" wget -q https://raw.githubusercontent.com/snapcore/models/master/ubuntu-core-"$version"-amd64-dangerous.model -O my.model @@ -230,6 +248,11 @@ execute: | remote.push optional-install.json muinstaller_args="$muinstaller_args -optional ./optional-install.json" fi + if [ -n "$PASSPHRASE" ]; then + # pbkdf2 is used because it is less memory intensive than in + # process argon2i* variants. + muinstaller_args="$muinstaller_args -passphrase $PASSPHRASE --kdf-type pbkdf2" + fi remote.exec "sudo muinstaller $muinstaller_args" @@ -245,7 +268,11 @@ execute: | mv fake-disk.img "$NESTED_IMAGES_DIR/$IMAGE_NAME" # Start installed image - tests.nested create-vm core --keep-firmware-state + if [ -n "$PASSPHRASE" ]; then + tests.nested create-vm core --keep-firmware-state --expect-passphrase "$PASSPHRASE" + else + tests.nested create-vm core --keep-firmware-state + fi # things look fine remote.exec "cat /etc/os-release" | MATCH 'NAME="Ubuntu Core"' From d897ea65176f3e0e4d6ed106e94afe36bc9e7c8b Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Fri, 20 Dec 2024 15:28:24 +0200 Subject: [PATCH 3/9] fixup! tests: exit if qemu does not support socket serial Signed-off-by: Zeyad Gouda --- tests/lib/nested.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/nested.sh b/tests/lib/nested.sh index 1e2c9ba1ab9..868646666d6 100755 --- a/tests/lib/nested.sh +++ b/tests/lib/nested.sh @@ -1216,6 +1216,7 @@ nested_start_core_vm_unit() { if "$QEMU" -version | grep '2\.5'; then if [ -z "$EXPECT_PASSPHRASE" ]; then echo "internal error: NESTED_EXPECT_PASSPHRASE is set and qemu doesn't support chardev over socket" + exit 1 fi # XXX: remove once we no longer support xenial hosts PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log" From a4cae1c0e6e72d2b22690e00966582b2303f3c94 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Fri, 20 Dec 2024 15:28:56 +0200 Subject: [PATCH 4/9] fixup! tests: replace jq with gojq Signed-off-by: Zeyad Gouda --- .../passphrase-support-on-hybrid/task.yaml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml index b71c54c730e..4c88af53598 100644 --- a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml +++ b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml @@ -38,8 +38,6 @@ prepare: | exit fi apt install dosfstools kpartx - snap install jq - snap install yq # Fakestore is needed for "snap prepare-image". "$TESTSTOOLS"/store-state setup-fake-store "$STORE_DIR" @@ -108,23 +106,23 @@ execute: | remote.exec "sudo blkid /dev/disk/by-label/ubuntu-data-enc" | MATCH crypto_LUKS # Check disk mappings. - remote.exec "sudo snap install jq" # TODO: no ubuntu-save right now because: # "ERROR cannot store device key pair: internal error: cannot access device keypair manager if ubuntu-save is unavailable" #DISK_MAPPINGS=(/run/mnt/ubuntu-save/device/disk-mapping.json # /run/mnt/data/var/lib/snapd/device/disk-mapping.json) DISK_MAPPINGS=(/run/mnt/data/var/lib/snapd/device/disk-mapping.json) for DM in "${DISK_MAPPINGS[@]}"; do - remote.exec "sudo cat $DM | jq '.pc.\"structure-encryption\".\"ubuntu-save\".method'" | MATCH '"LUKS"' - remote.exec "sudo cat $DM | jq '.pc.\"structure-encryption\".\"ubuntu-data\".method'" | MATCH '"LUKS"' + remote.exec "sudo cat $DM" > mapping.json + gojq -r '.pc."structure-encryption"."ubuntu-save".method' < mapping.json | MATCH LUKS + gojq -r '.pc."structure-encryption"."ubuntu-data".method' < mapping.json | MATCH LUKS done # Check that on an already provisioned system the API will give a # sensible reason why the system cannot be installed without further # action. remote.exec "sudo snap debug api /v2/systems/classic" > system - jq '.result."storage-encryption".support' < system | MATCH "unavailable" - jq '.result."storage-encryption"."unavailable-reason"' < system | MATCH "not encrypting device storage as checking TPM gave: the TPM is in DA lockout mode" + gojq '.result."storage-encryption".support' < system | MATCH "unavailable" + gojq '.result."storage-encryption"."unavailable-reason"' < system | MATCH "not encrypting device storage as checking TPM gave: the TPM is in DA lockout mode" # refresh rebooting snap # $1: path to snap file @@ -163,12 +161,12 @@ execute: | # Initial reseal count. remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains-before.json - reseal_count_start="$(jq -r '.["reseal-count"]' < boot-chains-before.json )" + reseal_count_start="$(gojq -r '.["reseal-count"]' < boot-chains-before.json )" fetch_and_check_reseal_count_equal() { local reseal_count_now remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains.json - reseal_count_now="$(jq -r '.["reseal-count"]' < boot-chains.json )" + reseal_count_now="$(gojq -r '.["reseal-count"]' < boot-chains.json )" test "$reseal_count_now" = "$1" } From 522f76732e2109651b98f9dd7f4cf9ce8c3d7b7f Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Fri, 20 Dec 2024 18:17:38 +0200 Subject: [PATCH 5/9] fixup! tests: fix unbound PASSPHRASE variable for default case Signed-off-by: Zeyad Gouda --- tests/nested/manual/muinstaller-core/task.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/nested/manual/muinstaller-core/task.yaml b/tests/nested/manual/muinstaller-core/task.yaml index 94fd545ed33..3830415742b 100644 --- a/tests/nested/manual/muinstaller-core/task.yaml +++ b/tests/nested/manual/muinstaller-core/task.yaml @@ -14,6 +14,9 @@ environment: NESTED_ENABLE_TPM: true NESTED_ENABLE_SECURE_BOOT: true + # by default, we don't do passphrase authentication + PASSPHRASE: "" + # encrypted + passphrase auth smoke test # TODO: extract a more comprehensive core24 test similar # to tests/nested/manual/passphrase-support-on-hybrid. From 0f3c30040e6ceb23182174bbce2e3a919f3e8a16 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Tue, 14 Jan 2025 13:27:58 +0200 Subject: [PATCH 6/9] fixup! tests: address review comments Signed-off-by: Zeyad Gouda --- .../remote/remote.wait-for | 2 +- tests/lib/nested.sh | 4 +- tests/lib/tools/setup_nested_hybrid_system.sh | 11 ++-- .../nested/manual/muinstaller-core/task.yaml | 1 - .../nested/manual/muinstaller-real/task.yaml | 1 - .../passphrase-support-on-hybrid/task.yaml | 61 +++++++------------ 6 files changed, 31 insertions(+), 49 deletions(-) diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for index 0860290462f..fcaafa1d492 100755 --- a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for +++ b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for @@ -123,7 +123,7 @@ wait_for_reboot() { # Wait for passphrase prompt from serial log file. if MATCH "Please enter the passphrase" < ${log_file}; then # Enter passphrase to continue boot. - echo "$expect_passphrase" | netcat -N localhost 7777 + echo "$expect_passphrase" > /dev/tcp/localhost/7777 break fi sleep "$wait" diff --git a/tests/lib/nested.sh b/tests/lib/nested.sh index 868646666d6..edaf7116129 100755 --- a/tests/lib/nested.sh +++ b/tests/lib/nested.sh @@ -1382,7 +1382,7 @@ nested_start_core_vm_unit() { # Wait for passphrase prompt from serial log file. retry -n 120 --wait 1 sh -c "MATCH \"Please enter the passphrase\" < ${NESTED_LOGS_DIR}/serial.log" # Enter passphrase to continue boot. - echo "$EXPECT_PASSPHRASE" | netcat -N localhost 7777 + echo "$EXPECT_PASSPHRASE" > /dev/tcp/localhost/7777 fi local EXPECT_SHUTDOWN @@ -1617,7 +1617,7 @@ nested_start_classic_vm() { # XXX: remove once we no longer support xenial hosts PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log" else - PARAM_SERIAL="-chardev socket,telnet=on,host=localhost,server=on,port=7777,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0" + PARAM_SERIAL="-chardev socket,path=${NESTED_LOGS_DIR}/serial.sock,server=on,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0" fi PARAM_BIOS="" PARAM_TPM="" diff --git a/tests/lib/tools/setup_nested_hybrid_system.sh b/tests/lib/tools/setup_nested_hybrid_system.sh index e3352c367dd..379eda599cd 100755 --- a/tests/lib/tools/setup_nested_hybrid_system.sh +++ b/tests/lib/tools/setup_nested_hybrid_system.sh @@ -21,7 +21,8 @@ run_muinstaller() { local disk="${8}" local kern_mods_comp="${9:-}" local passphrase="${10}" - local extra_muinstaller_args="${11}" + shift 10 + local extra_muinstaller_args=("${@}") # ack the needed assertions snap ack "${kernel_assertion}" @@ -221,7 +222,7 @@ main() { local disk="" local kern_mods_comp="" local passphrase="" - local extra_muinstaller_args="" + local extra_muinstaller_args=() while [ $# -gt 0 ]; do case "$1" in --model) @@ -265,8 +266,8 @@ main() { passphrase="${2}" shift 2 ;; - --extra-muinstaller-args) - extra_muinstaller_args="${2}" + --extra-muinstaller-arg) + extra_muinstaller_args+=("${2}") shift 2 ;; --*|-*) @@ -359,7 +360,7 @@ main() { run_muinstaller "${model_assertion}" "${store_dir}" "${gadget_snap}" \ "${gadget_assertion}" "${kernel_snap}" "${kernel_assertion}" "${label}" \ - "${disk}" "${kern_mods_comp}" "${passphrase}" "${extra_muinstaller_args}" + "${disk}" "${kern_mods_comp}" "${passphrase}" "${extra_muinstaller_args[@]}" ) } diff --git a/tests/nested/manual/muinstaller-core/task.yaml b/tests/nested/manual/muinstaller-core/task.yaml index 3830415742b..51114e2b8a7 100644 --- a/tests/nested/manual/muinstaller-core/task.yaml +++ b/tests/nested/manual/muinstaller-core/task.yaml @@ -55,7 +55,6 @@ prepare: | echo "This test needs test keys to be trusted" exit fi - apt install dosfstools restore: | rm -rf ./classic-root diff --git a/tests/nested/manual/muinstaller-real/task.yaml b/tests/nested/manual/muinstaller-real/task.yaml index 7fec476a21b..822fbbf6e1f 100644 --- a/tests/nested/manual/muinstaller-real/task.yaml +++ b/tests/nested/manual/muinstaller-real/task.yaml @@ -39,7 +39,6 @@ prepare: | echo "This test needs test keys to be trusted" exit fi - apt install dosfstools kpartx "$TESTSTOOLS"/store-state setup-fake-store "$STORE_DIR" restore: | diff --git a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml index 4c88af53598..d22b4da963f 100644 --- a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml +++ b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml @@ -19,7 +19,8 @@ environment: KERNEL_CHANNEL: 24.10/edge CORE_VERSION: 22 - PASSPHRASE: ubuntu + # Check if passphrase with space is handled properly + PASSPHRASE: "ubuntu test" # "pbkdf2" is less memory intensive than argon2i* so we use it by default. # TODO: Add argon2i* variants. KDF_TYPE: pbkdf2 @@ -37,7 +38,6 @@ prepare: | echo "This test needs test keys to be trusted" exit fi - apt install dosfstools kpartx # Fakestore is needed for "snap prepare-image". "$TESTSTOOLS"/store-state setup-fake-store "$STORE_DIR" @@ -91,7 +91,7 @@ execute: | --kernel pc-kernel.snap \ --kernel-assertion pc-kernel.assert \ --passphrase "$PASSPHRASE" \ - --extra-muinstaller-args "-kdf-type $KDF_TYPE" \ + --extra-muinstaller-arg "-kdf-type $KDF_TYPE" \ --disk disk.img # Basic things look fine. @@ -117,22 +117,13 @@ execute: | gojq -r '.pc."structure-encryption"."ubuntu-data".method' < mapping.json | MATCH LUKS done - # Check that on an already provisioned system the API will give a - # sensible reason why the system cannot be installed without further - # action. - remote.exec "sudo snap debug api /v2/systems/classic" > system - gojq '.result."storage-encryption".support' < system | MATCH "unavailable" - gojq '.result."storage-encryption"."unavailable-reason"' < system | MATCH "not encrypting device storage as checking TPM gave: the TPM is in DA lockout mode" - # refresh rebooting snap # $1: path to snap file # $2: snap name - # $3: reboot action ("reboot"/"no-reboot") refresh_rebooting_snap() { local snap_filename=$1 local snap_name=$2 - local reboot_action=$3 boot_id=$(tests.nested boot-id) @@ -144,31 +135,19 @@ execute: | # Check that a reboot notification was setup. remote.exec test -f /run/reboot-required remote.exec cat /run/reboot-required.pkgs | MATCH "snap:${snap_name}" - # Check that no reboot has been scheduled, then force a reboot - remote.exec not test -f /run/systemd/shutdown/scheduled - - if [ "$reboot_action" = "reboot" ]; then - local log_file="$NESTED_LOGS_DIR"/serial.log - # Clear old log file to avoid matching passphrase prompt from previous boot. - echo "" > "$log_file" - remote.exec sudo reboot || true - remote.wait-for reboot --wait 1 -n 200 --expect-passphrase "ubuntu" --log-file "$log_file" "$boot_id" - remote.exec sudo snap watch --last=install - fi + + local log_file="$NESTED_LOGS_DIR"/serial.log + # Clear old log file to avoid matching passphrase prompt from previous boot. + echo "" > "$log_file" + remote.exec sudo reboot || true + remote.wait-for reboot --wait 1 -n 200 --expect-passphrase "$PASSPHRASE" --log-file "$log_file" "$boot_id" + remote.exec sudo snap watch --last=install } # Ensure update-notifier-common is installed so that reboot notification works. remote.exec "sudo apt install -y update-notifier-common" - # Initial reseal count. - remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains-before.json - reseal_count_start="$(gojq -r '.["reseal-count"]' < boot-chains-before.json )" - - fetch_and_check_reseal_count_equal() { - local reseal_count_now - remote.exec sudo cat /var/lib/snapd/device/fde/boot-chains > boot-chains.json - reseal_count_now="$(gojq -r '.["reseal-count"]' < boot-chains.json )" - test "$reseal_count_now" = "$1" - } + # Save PCR profile + remote.exec "sudo cat /var/lib/snapd/state.json" | gojq -r '.data.fde."keyslot-roles".run.params.all."tpm2-pcr-profile"' > pcr_profile # 1. Test gadget refresh causing reseal. @@ -176,19 +155,23 @@ execute: | echo 'console=ttyS0 systemd.journald.forward_to_console=1 loglevel=4' > pc-gadget/cmdline.extra tests.nested secboot-sign gadget pc-gadget "$SNAKEOIL_KEY" "$SNAKEOIL_CERT" snap pack --filename=pc-new.snap pc-gadget/ - refresh_rebooting_snap pc-new.snap pc reboot + refresh_rebooting_snap pc-new.snap pc - # We expect two reseals, one before the update and one after. - fetch_and_check_reseal_count_equal "$((reseal_count_start + 2))" + # We expect a reseals, PCR profile should have been updated. + remote.exec "sudo cat /var/lib/snapd/state.json" | gojq -r '.data.fde."keyslot-roles".run.params.all."tpm2-pcr-profile"' > pcr_profile_current + not diff pcr_profile pcr_profile_current + mv pcr_profile_current pcr_profile # 2. Test kernel refresh causing reseal. # Resigning kernel should be enough to trigger a reseal. uc24_build_initramfs_kernel_snap "$PWD/pc-kernel.snap" "$PWD/pc-kernel-new.snap" - refresh_rebooting_snap pc-kernel-new.snap pc reboot + refresh_rebooting_snap pc-kernel-new.snap pc - # We expect two reseals, one before the update and one after. - fetch_and_check_reseal_count_equal "$((reseal_count_start + 4))" + # We expect a reseals, PCR profile should have been updated. + remote.exec "sudo cat /var/lib/snapd/state.json" | gojq -r '.data.fde."keyslot-roles".run.params.all."tpm2-pcr-profile"' > pcr_profile_current + not diff pcr_profile pcr_profile_current + mv pcr_profile_current pcr_profile # TODO: 3. Try refreshing to an unsupported kernel when snapd-info files # are available. From 1d1a2df6a56dab16ec7778839c9e3142fe9a549b Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Tue, 28 Jan 2025 10:21:09 +0200 Subject: [PATCH 7/9] fixup! tests: revert to using netcat for sending passphrase to nested vm using tcp socket file caused the VM to keep waiting Signed-off-by: Zeyad Gouda --- tests/lib/external/snapd-testing-tools/remote/remote.wait-for | 4 ++-- tests/lib/nested.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for index fcaafa1d492..211bb4023a5 100755 --- a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for +++ b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for @@ -1,4 +1,4 @@ -#!/bin/bash -e +#!/bin/bash -xe # The default values have been selected trying to match with most of # the wait times in the tests and also trying to follow common sense. @@ -123,7 +123,7 @@ wait_for_reboot() { # Wait for passphrase prompt from serial log file. if MATCH "Please enter the passphrase" < ${log_file}; then # Enter passphrase to continue boot. - echo "$expect_passphrase" > /dev/tcp/localhost/7777 + echo "$expect_passphrase" | netcat -N localhost 7777 break fi sleep "$wait" diff --git a/tests/lib/nested.sh b/tests/lib/nested.sh index edaf7116129..868646666d6 100755 --- a/tests/lib/nested.sh +++ b/tests/lib/nested.sh @@ -1382,7 +1382,7 @@ nested_start_core_vm_unit() { # Wait for passphrase prompt from serial log file. retry -n 120 --wait 1 sh -c "MATCH \"Please enter the passphrase\" < ${NESTED_LOGS_DIR}/serial.log" # Enter passphrase to continue boot. - echo "$EXPECT_PASSPHRASE" > /dev/tcp/localhost/7777 + echo "$EXPECT_PASSPHRASE" | netcat -N localhost 7777 fi local EXPECT_SHUTDOWN @@ -1617,7 +1617,7 @@ nested_start_classic_vm() { # XXX: remove once we no longer support xenial hosts PARAM_SERIAL="-serial file:${NESTED_LOGS_DIR}/serial.log" else - PARAM_SERIAL="-chardev socket,path=${NESTED_LOGS_DIR}/serial.sock,server=on,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0" + PARAM_SERIAL="-chardev socket,telnet=on,host=localhost,server=on,port=7777,wait=off,id=char0,logfile=${NESTED_LOGS_DIR}/serial.log,logappend=on -serial chardev:char0" fi PARAM_BIOS="" PARAM_TPM="" From 685389af738d0d6246662d73fdbd8bad69368377 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Tue, 4 Feb 2025 17:46:09 +0200 Subject: [PATCH 8/9] fixup! tests: revert remote.wait-for changes Signed-off-by: Zeyad Gouda --- .../remote/remote.wait-for | 43 +++---------------- .../passphrase-support-on-hybrid/task.yaml | 11 +++-- 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for index 211bb4023a5..a16179d8b1b 100755 --- a/tests/lib/external/snapd-testing-tools/remote/remote.wait-for +++ b/tests/lib/external/snapd-testing-tools/remote/remote.wait-for @@ -1,4 +1,4 @@ -#!/bin/bash -xe +#!/bin/bash -e # The default values have been selected trying to match with most of # the wait times in the tests and also trying to follow common sense. @@ -17,7 +17,7 @@ show_help() { echo "usage: remote.wait-for ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for no-ssh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for snap-command [--wait WAIT] [-n|--attempts ATTEMPTS]" - echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS] [--expect-passphrase PASSPHRASE] [--log-file PATH]" + echo " remote.wait-for reboot [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for device-initialized [--wait WAIT] [-n|--attempts ATTEMPTS]" echo " remote.wait-for refresh [--wait WAIT] [-n|--attempts ATTEMPTS]" echo "" @@ -99,9 +99,7 @@ wait_for_reconnect_ssh() { wait_for_reboot() { local attempts=${1:-$DEFAULT_WAIT_FOR_REBOOT_ATTEMPTS} local wait=${2:-$DEFAULT_WAIT_FOR_REBOOT_WAIT} - local expect_passphrase=${3:-} - local log_file=${4:-} - local initial_boot_id=$5 + local initial_boot_id=$3 local last_boot_id echo "remote.wait-for: waiting for reboot" @@ -112,24 +110,6 @@ wait_for_reboot() { return fi - if [ -n "$expect_passphrase" ]; then - if [ ! -f "$log_file" ]; then - echo "remote.wait-for --expect-passphrase without passing an existing --log-file" - return 1 - fi - while [ "$attempts" -ge 0 ]; do - echo -n '.' - attempts=$(( attempts - 1 )) - # Wait for passphrase prompt from serial log file. - if MATCH "Please enter the passphrase" < ${log_file}; then - # Enter passphrase to continue boot. - echo "$expect_passphrase" | netcat -N localhost 7777 - break - fi - sleep "$wait" - done - fi - while [ "$attempts" -ge 0 ]; do echo -n '.' attempts=$(( attempts - 1 )) @@ -143,10 +123,7 @@ wait_for_reboot() { done echo "" - if [ -z "$last_boot_id" ]; then - echo "remote.wait-for: could not get last boot id" - return 1 - elif [ "$last_boot_id" != "$initial_boot_id" ]; then + if [ "$last_boot_id" != "$initial_boot_id" ]; then echo "remote.wait-for: reboot completed" else echo "remote.wait-for: boot id did not change" @@ -264,7 +241,7 @@ main() { exit fi - local action wait attempts expect_passphrase log_file others + local action wait attempts others case "$1" in -h|--help) show_help @@ -316,14 +293,6 @@ main() { attempts=$2 shift 2 ;; - --expect-passphrase) - expect_passphrase="$2" - shift 2 - ;; - --log-file) - log_file="$2" - shift 2 - ;; *) if [ -z "$others" ]; then others=$1 @@ -335,7 +304,7 @@ main() { esac done - "$action" "$attempts" "$wait" "$expect_passphrase" "$log_file" "$others" + "$action" "$attempts" "$wait" "$others" } main "$@" diff --git a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml index d22b4da963f..aac6c969cc7 100644 --- a/tests/nested/manual/passphrase-support-on-hybrid/task.yaml +++ b/tests/nested/manual/passphrase-support-on-hybrid/task.yaml @@ -136,11 +136,16 @@ execute: | remote.exec test -f /run/reboot-required remote.exec cat /run/reboot-required.pkgs | MATCH "snap:${snap_name}" - local log_file="$NESTED_LOGS_DIR"/serial.log # Clear old log file to avoid matching passphrase prompt from previous boot. - echo "" > "$log_file" + echo "" > "$NESTED_LOGS_DIR"/serial.log remote.exec sudo reboot || true - remote.wait-for reboot --wait 1 -n 200 --expect-passphrase "$PASSPHRASE" --log-file "$log_file" "$boot_id" + + # Wait for passphrase prompt + retry -n 120 --wait 1 sh -c "MATCH \"Please enter the passphrase\" < ${NESTED_LOGS_DIR}/serial.log" + # Enter passphrase to continue boot + echo "$PASSPHRASE" | netcat -N localhost 7777 + + remote.wait-for reboot --wait 1 -n 100 "$boot_id" remote.exec sudo snap watch --last=install } # Ensure update-notifier-common is installed so that reboot notification works. From 6678a7fcadc1ee89bb6f6463977d23d064b2cc97 Mon Sep 17 00:00:00 2001 From: Zeyad Gouda Date: Wed, 5 Feb 2025 12:51:39 +0200 Subject: [PATCH 9/9] fixup! tests: address review comments (thanks @alfonsosanchezbeato) Signed-off-by: Zeyad Gouda --- tests/lib/nested.sh | 14 ++++++++------ tests/lib/tools/setup_nested_hybrid_system.sh | 10 +++++----- tests/lib/tools/tests.nested | 10 +++++----- tests/nested/manual/muinstaller-core/task.yaml | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/lib/nested.sh b/tests/lib/nested.sh index 868646666d6..c38c440c118 100755 --- a/tests/lib/nested.sh +++ b/tests/lib/nested.sh @@ -24,6 +24,8 @@ : "${NESTED_DISK_PHYSICAL_BLOCK_SIZE:=512}" : "${NESTED_DISK_LOGICAL_BLOCK_SIZE:=512}" +: "${NESTED_PASSPHRASE:=}" + nested_wait_for_ssh() { local retry=${1:-800} local wait=${2:-1} @@ -1204,8 +1206,8 @@ nested_start_core_vm_unit() { PARAM_RTC="${NESTED_PARAM_RTC:-}" PARAM_EXTRA="${NESTED_PARAM_EXTRA:-}" - local EXPECT_PASSPHRASE - EXPECT_PASSPHRASE=${NESTED_EXPECT_PASSPHRASE:-} + local PASSPHRASE + PASSPHRASE=${NESTED_PASSPHRASE:-} # Open port 7777 on the host so that failures in the nested VM (e.g. to # create users) can be debugged interactively via @@ -1214,8 +1216,8 @@ nested_start_core_vm_unit() { # XXX: should serial just be logged to stdout so that we just need # to "journalctl -u $NESTED_VM" to see what is going on ? if "$QEMU" -version | grep '2\.5'; then - if [ -z "$EXPECT_PASSPHRASE" ]; then - echo "internal error: NESTED_EXPECT_PASSPHRASE is set and qemu doesn't support chardev over socket" + if [ -z "$PASSPHRASE" ]; then + echo "internal error: NESTED_PASSPHRASE is set and qemu doesn't support chardev over socket" exit 1 fi # XXX: remove once we no longer support xenial hosts @@ -1378,11 +1380,11 @@ nested_start_core_vm_unit() { ${PARAM_CD} \ ${PARAM_EXTRA} " "${PARAM_REEXEC_ON_FAILURE}" - if [ -n "$EXPECT_PASSPHRASE" ]; then + if [ -n "$PASSPHRASE" ]; then # Wait for passphrase prompt from serial log file. retry -n 120 --wait 1 sh -c "MATCH \"Please enter the passphrase\" < ${NESTED_LOGS_DIR}/serial.log" # Enter passphrase to continue boot. - echo "$EXPECT_PASSPHRASE" | netcat -N localhost 7777 + echo "$PASSPHRASE" | netcat -N localhost 7777 fi local EXPECT_SHUTDOWN diff --git a/tests/lib/tools/setup_nested_hybrid_system.sh b/tests/lib/tools/setup_nested_hybrid_system.sh index 379eda599cd..249c939ae16 100755 --- a/tests/lib/tools/setup_nested_hybrid_system.sh +++ b/tests/lib/tools/setup_nested_hybrid_system.sh @@ -168,11 +168,11 @@ EOF remote.exec "chmod +x /home/user1/mk-classic-rootfs-wrapper.sh" muinstaller_args=() - muinstaller_args+=("-label $label") - muinstaller_args+=("-device $install_disk") - muinstaller_args+=("-rootfs-creator /home/user1/mk-classic-rootfs-wrapper.sh") + muinstaller_args+=("-label" "$label") + muinstaller_args+=("-device" "$install_disk") + muinstaller_args+=("-rootfs-creator" "/home/user1/mk-classic-rootfs-wrapper.sh") if [ -n "$passphrase" ]; then - muinstaller_args+=("-passphrase \"$passphrase\"") + muinstaller_args+=("-passphrase" "\"$passphrase\"") fi muinstaller_args+=("${extra_muinstaller_args[@]}") remote.exec sudo muinstaller "${muinstaller_args[@]}" @@ -205,7 +205,7 @@ EOF # Start installed image if [ -n "$passphrase" ]; then - tests.nested create-vm core --keep-firmware-state --expect-passphrase "$passphrase" + tests.nested create-vm core --keep-firmware-state --passphrase "$passphrase" else tests.nested create-vm core --keep-firmware-state fi diff --git a/tests/lib/tools/tests.nested b/tests/lib/tools/tests.nested index 535ecd42f13..63bd21cbc4e 100755 --- a/tests/lib/tools/tests.nested +++ b/tests/lib/tools/tests.nested @@ -133,7 +133,7 @@ create_vm() { ;; esac - local NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE + local NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_PASSPHRASE while [ $# -gt 0 ]; do case "$1" in --param-cdrom) @@ -156,8 +156,8 @@ create_vm() { NESTED_KEEP_FIRMWARE_STATE=1 shift ;; - --expect-passphrase) - NESTED_EXPECT_PASSPHRASE="$2" + --passphrase) + NESTED_PASSPHRASE="$2" shift 2 ;; *) @@ -167,9 +167,9 @@ create_vm() { esac done - export NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE + export NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_PASSPHRASE "$action" - unset NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_EXPECT_PASSPHRASE + unset NESTED_PARAM_CD NESTED_CPUS NESTED_MEM NESTED_PARAM_EXTRA NESTED_KEEP_FIRMWARE_STATE NESTED_PASSPHRASE } is_nested() { diff --git a/tests/nested/manual/muinstaller-core/task.yaml b/tests/nested/manual/muinstaller-core/task.yaml index 51114e2b8a7..ec7199d6764 100644 --- a/tests/nested/manual/muinstaller-core/task.yaml +++ b/tests/nested/manual/muinstaller-core/task.yaml @@ -271,7 +271,7 @@ execute: | # Start installed image if [ -n "$PASSPHRASE" ]; then - tests.nested create-vm core --keep-firmware-state --expect-passphrase "$PASSPHRASE" + tests.nested create-vm core --keep-firmware-state --passphrase "$PASSPHRASE" else tests.nested create-vm core --keep-firmware-state fi