1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
#!/bin/sh
# Copyright 2019 Johannes 'josch' Schauer <josch@debian.org>
# Copyright 2021 Guilhem Moulin <guilhem@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
set -eux
PATH="/usr/bin:/bin"
export PATH
TESTNAME="$(basename -- "$0")"
# get package version/distribution and local autopkgtest repo URI to
# pass along to mmdebstrap
VERSION="$(dpkg-parsechangelog -SVersion)"
apt-cache show "dropbear=$VERSION" >/dev/null || exit 1
# try to create /dev/kvm if missing, for instance in a schroot where /dev isn't managed by udev
if [ ! -c /dev/kvm ] && mknod -m0600 /dev/kvm c 10 232; then
echo "INFO: Created character special file /dev/kvm" >&2
if getent group kvm >/dev/null && chgrp kvm /dev/kvm; then
# kvm group is created by udev.postinst
chmod 0640 /dev/kvm
fi
fi
if [ -c /dev/kvm ] && dd if=/dev/kvm count=0 status=none; then
CPU_MODEL="host"
else
CPU_MODEL="max"
echo "WARN: KVM is not available, guests will be slow!" >&2
fi
# mkinitramfs(8) uses the default kernel by default, but we might be running an
# old kernel and need to use what's defined as dependency for this test anyway
DEB_HOST_ARCH="$(dpkg-architecture -qDEB_HOST_ARCH)"
KERNEL_VERSION="$(find /lib/modules -mindepth 1 -maxdepth 1 -name "*-$DEB_HOST_ARCH" -type d -printf "%P\\n" | sort -V | tail -n1)"
KERNEL="/boot/vmlinuz-$KERNEL_VERSION"
if [ -z "$KERNEL_VERSION" ] || [ ! -f "$KERNEL" ]; then
echo "ERROR: Could not determine kernel to use, aborting" >&2
exit 1
fi
# generate key file used to format and unlock the root FS
head -c32 </dev/urandom >"$AUTOPKGTEST_TMP/rootfs.key"
# rebuild initramfs with custom hook and boot scripts
# (note: cryptsetup-initramfs might spew warnings depending on the host's device stack -- these are irrelevant here)
install -m0755 "$0.d/initramfs-hook" "/etc/initramfs-tools/hooks/autopkgtest-$TESTNAME-setup"
install -m0755 "$0.d/initramfs-script" "/etc/initramfs-tools/scripts/init-premount/autopkgtest-$TESTNAME-setup"
/usr/sbin/mkinitramfs -o "$AUTOPKGTEST_TMP/initrd.img" "$KERNEL_VERSION"
rm -f "/etc/initramfs-tools/hooks/autopkgtest-$TESTNAME-setup" "/etc/initramfs-tools/scripts/init-premount/autopkgtest-$TESTNAME-setup"
# generate SSH key used to log in to the guest
ssh-keygen -t rsa -C "$TESTNAME" -f "$AUTOPKGTEST_TMP/id_rsa" -N ""
# prepare chroot tarball with a minimal system (to find out missing
# dependencies); we do not use debootstrap because it's unable to create
# a system containing only essential:yes packages and their dependencies,
# and don't use multistrap because it's unmaintained
#
# aside from obious packages (kernel, boot loader, cryptsetup-initramfs, dropbear*)
# we need the following:
# - e2fsprogs: for /sbin/fsck.ext4
# - systemd-sysv: for /sbin/init
# - systemd-cryptsetup: to unlock the swap device at boot time
# - ifupdown: for networking
# (note: cryptsetup-initramfs and/or dropbear-initramfs might spew warnings depending on
# the host's device stack -- these are irrelevant here)
apt-get indextargets --format "deb \$(SITE) \$(SUITE) \$(COMPONENT)" \
"Created-By: Packages" "Origin: Debian" >"$AUTOPKGTEST_TMP/sources.list"
AUTOPKGTEST_LOCAL_ARCHIVE="$(apt-get indextargets --format "\$(REPO_URI)" "Created-By: Packages" | \
sed -nr '\,^(file|copy):((/+[^/[:blank:]]+)*/autopkgtest[.-].*[^/])/?$, {s//\2/p;q}')"
if [ -n "$AUTOPKGTEST_LOCAL_ARCHIVE" ]; then
echo "deb [trusted=yes] file://$AUTOPKGTEST_LOCAL_ARCHIVE /" >>"$AUTOPKGTEST_TMP/sources.list"
fi
mmdebstrap --verbose --variant="apt" \
--include="linux-image-$KERNEL_VERSION" \
--include="grub2" \
--include="cryptsetup-initramfs" \
--include="dropbear=$VERSION,dropbear-initramfs=$VERSION" \
--include="e2fsprogs" \
--include="systemd-sysv" \
--include="systemd-cryptsetup" \
--include="ifupdown" \
--customize-hook="upload \"$AUTOPKGTEST_TMP/id_rsa.pub\" /etc/dropbear/initramfs/authorized_keys" \
${AUTOPKGTEST_LOCAL_ARCHIVE:+--setup-hook="mkdir -p -- \"\$1${AUTOPKGTEST_LOCAL_ARCHIVE%/*}\"" \
--setup-hook="copy-in $AUTOPKGTEST_LOCAL_ARCHIVE ${AUTOPKGTEST_LOCAL_ARCHIVE%/*}" \
--customize-hook="rm -rf -- \"\$1${AUTOPKGTEST_LOCAL_ARCHIVE%/*}\"" \
--customize-hook="sed -i 's,.*\\bfile://,#&,' \"\$1/etc/apt/sources.list\"" } \
<"$AUTOPKGTEST_TMP/sources.list" >"$AUTOPKGTEST_TMP/debian.tar"
# pre-allocate disk image for the guest
DRIVE_IMG="$AUTOPKGTEST_TMP/disk.img"
dd if=/dev/zero of="$DRIVE_IMG" bs=1M count=2048 conv=fsync,excl status=none
# wrapper around qemu-system, passing common options and an optional timeout
SMP="cpus=2"
MEMORY="1G"
unset TIMEOUT
qemu_system() {
local exe="qemu-system-x86_64" rv
${TIMEOUT:+timeout -k60s -sTERM "$TIMEOUT"} "$exe" -no-user-config -nodefaults \
-name "autopkgtest-$TESTNAME" -machine "type=q35,accel=kvm:tcg,graphics=off" \
-cpu "$CPU_MODEL" -smp "$SMP" -m "$MEMORY" \
-serial stdio -vga virtio -display none -monitor none \
-object "rng-random,id=rng0,filename=/dev/urandom" -device "virtio-rng-pci,rng=rng0" \
-drive "file=$DRIVE_IMG,format=raw,if=virtio,cache=unsafe" \
"$@" </dev/null && return 0 || rv=$?
# timeout(1) exits with status 124 on timeout, or with status 128+9 if
# the command is still running after 60s and a SIGKILL needs to be sent
if [ -n ${TIMEOUT:+x} ] && [ $rv -eq 124 -o $rv -eq $((128+9)) ]; then
echo "ERROR: $TIMEOUT timeout reached while running $exe" >&2
else
echo "ERROR: $exe exited with status $rv" >&2
fi
trap - EXIT # don't try to kill the terminated QEMU process
exit 1
}
# prepare the target system from our custom initrd, passing the chroot tarball as secondary disk
TIMEOUT=1800s qemu_system \
-drive "file=$AUTOPKGTEST_TMP/debian.tar,format=raw,if=virtio,readonly=on" \
-kernel "$KERNEL" -initrd "$AUTOPKGTEST_TMP/initrd.img" -append "console=ttyS0"
# boot the target system -- without the chroot tarball but with a network card instead, forward
# connections from host port 12222 resp. 10022 to the guest's SSHd at initramfs stage resp.
# after pivoting
HOST_FWD="hostfwd=tcp:127.0.0.1:10022-10.0.2.129:22,hostfwd=tcp:127.0.0.1:12222-10.0.2.130:2222"
TIMEOUT=600s qemu_system \
-netdev "user,restrict=on,id=net0,$HOST_FWD" -device "virtio-net-pci,netdev=net0" &
QEMU_PID=$!
trap "kill $QEMU_PID" EXIT # terminate the guest if this script exits first
# wrapper around ssh(1), passing common options
ssh() {
command ssh -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no \
-oConnectTimeout=5 -i "$AUTOPKGTEST_TMP/id_rsa" -l root "$@"
}
# keep trying to ssh into QEMU up to $MAX_TRIES times
unset MAX_TRIES
ssh_retry() {
local i=${MAX_TRIES:-24} rv
while :; do
ssh "$@" && rv=0 || rv=$?
if [ $rv -ne 255 ]; then
# the remote command was run, returns its exit status
return $rv
elif [ $i -le 1 ]; then
echo "ERROR: Couldn't SSH into QEMU: maximum number of tries exceeded" >&2
exit 1
fi
i=$((i - 1))
sleep 5
done
}
# wait for dropbear to start on the guest, then remotely unlock the system
ssh_retry -Tp 12222 "127.0.0.1" /nonexistent <"$AUTOPKGTEST_TMP/rootfs.key"
# wait for the main system to finish booting and its SSHd to start, then
# make sure the root FS and swap are held by dm-crypt devices
# XXX we have to use cryptsetup(8)'s full path here, see #903403
ssh_retry -nTp 10022 "127.0.0.1" echo ping
ssh -nTp 10022 "127.0.0.1" lsblk -no"MOUNTPOINT" /dev/mapper/root_crypt | grep -Fx "/"
#ssh -nTp 10022 "127.0.0.1" lsblk -no"MOUNTPOINT" /dev/mapper/swap_crypt | grep -Fx "[SWAP]"
ssh -Tp 10022 "127.0.0.1" /sbin/cryptsetup luksOpen --key-file=- \
--test-passphrase /dev/vda4 <"$AUTOPKGTEST_TMP/rootfs.key"
# power the guest down and wait for QEMU to gracefully terminate
ssh -nTp 10022 "127.0.0.1" systemctl poweroff
trap - EXIT
wait $QEMU_PID
|