Security hardening: bind kubeconfig server to localhost, mount hardening (noexec/nosuid/nodev on tmpfs), sysctl network hardening, kernel module loading lock after boot, SHA256 checksum verification for downloads, kernel AppArmor + Audit support, complain-mode AppArmor profiles for containerd and kubelet, and security integration test. ARM64 Raspberry Pi support: piCore64 base extraction, RPi kernel build from raspberrypi/linux fork, RPi firmware fetch, SD card image with 4- partition GPT and tryboot A/B mechanism, BootEnv Go interface abstracting GRUB vs RPi boot environments, architecture-aware build scripts, QEMU aarch64 dev VM and boot test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
161 lines
4.7 KiB
Bash
Executable File
161 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# run-vm.sh — Launch QEMU VM for testing (reusable by other test scripts)
|
|
# Usage: ./test/qemu/run-vm.sh <iso-or-img> [options]
|
|
#
|
|
# Options:
|
|
# --arch <arch> Architecture: x86_64 (default) or arm64
|
|
# --data-disk <path> Use existing data disk (default: create temp)
|
|
# --data-size <MB> Size of temp data disk (default: 1024)
|
|
# --memory <MB> VM memory (default: 2048)
|
|
# --cpus <n> VM CPUs (default: 2)
|
|
# --serial-log <path> Write serial output to file
|
|
# --api-port <port> Forward K8s API to host port (default: 6443)
|
|
# --ssh-port <port> Forward SSH to host port (default: 2222)
|
|
# --background Run in background, print PID
|
|
# --append <args> Extra kernel append args
|
|
# --kernel <path> Kernel image (required for arm64)
|
|
# --initrd <path> Initramfs image (required for arm64)
|
|
#
|
|
# Outputs (on stdout):
|
|
# QEMU_PID=<pid>
|
|
# DATA_DISK=<path>
|
|
# SERIAL_LOG=<path>
|
|
set -euo pipefail
|
|
|
|
IMAGE="${1:?Usage: $0 <iso-or-img> [options]}"
|
|
shift
|
|
|
|
# Defaults
|
|
ARCH="x86_64"
|
|
DATA_DISK=""
|
|
DATA_SIZE_MB=1024
|
|
MEMORY=2048
|
|
CPUS=2
|
|
SERIAL_LOG=""
|
|
API_PORT=6443
|
|
SSH_PORT=2222
|
|
BACKGROUND=0
|
|
EXTRA_APPEND=""
|
|
CREATED_DATA_DISK=""
|
|
VM_KERNEL=""
|
|
VM_INITRD=""
|
|
|
|
# Parse options
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--arch) ARCH="$2"; shift 2 ;;
|
|
--data-disk) DATA_DISK="$2"; shift 2 ;;
|
|
--data-size) DATA_SIZE_MB="$2"; shift 2 ;;
|
|
--memory) MEMORY="$2"; shift 2 ;;
|
|
--cpus) CPUS="$2"; shift 2 ;;
|
|
--serial-log) SERIAL_LOG="$2"; shift 2 ;;
|
|
--api-port) API_PORT="$2"; shift 2 ;;
|
|
--ssh-port) SSH_PORT="$2"; shift 2 ;;
|
|
--background) BACKGROUND=1; shift ;;
|
|
--append) EXTRA_APPEND="$2"; shift 2 ;;
|
|
--kernel) VM_KERNEL="$2"; shift 2 ;;
|
|
--initrd) VM_INITRD="$2"; shift 2 ;;
|
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# Create data disk if not provided
|
|
if [ -z "$DATA_DISK" ]; then
|
|
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
|
|
CREATED_DATA_DISK="$DATA_DISK"
|
|
dd if=/dev/zero of="$DATA_DISK" bs=1M count="$DATA_SIZE_MB" 2>/dev/null
|
|
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
|
|
fi
|
|
|
|
# Create serial log if not provided
|
|
if [ -z "$SERIAL_LOG" ]; then
|
|
SERIAL_LOG=$(mktemp /tmp/kubesolo-serial-XXXXXX.log)
|
|
fi
|
|
|
|
# Build QEMU command based on architecture
|
|
if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
|
|
# ARM64: qemu-system-aarch64 with -machine virt
|
|
# No KVM for cross-arch emulation (TCG only)
|
|
CONSOLE="ttyAMA0"
|
|
|
|
# ARM64 requires explicit kernel + initrd (no -cdrom support with -machine virt)
|
|
if [ -z "$VM_KERNEL" ] || [ -z "$VM_INITRD" ]; then
|
|
echo "ERROR: ARM64 mode requires --kernel and --initrd options" >&2
|
|
exit 1
|
|
fi
|
|
|
|
QEMU_CMD=(
|
|
qemu-system-aarch64
|
|
-machine virt
|
|
-cpu cortex-a72
|
|
-m "$MEMORY"
|
|
-smp "$CPUS"
|
|
-nographic
|
|
-net nic,model=virtio
|
|
-net "user,hostfwd=tcp::${API_PORT}-:6443,hostfwd=tcp::${SSH_PORT}-:22"
|
|
-drive "file=$DATA_DISK,format=raw,if=virtio"
|
|
-serial "file:$SERIAL_LOG"
|
|
-kernel "$VM_KERNEL"
|
|
-initrd "$VM_INITRD"
|
|
-append "console=${CONSOLE} kubesolo.data=/dev/vda kubesolo.debug $EXTRA_APPEND"
|
|
)
|
|
else
|
|
# x86_64: standard QEMU
|
|
CONSOLE="ttyS0,115200n8"
|
|
|
|
# Detect KVM availability
|
|
KVM_FLAG=""
|
|
if [ -w /dev/kvm ] 2>/dev/null; then
|
|
KVM_FLAG="-enable-kvm"
|
|
fi
|
|
|
|
QEMU_CMD=(
|
|
qemu-system-x86_64
|
|
-m "$MEMORY"
|
|
-smp "$CPUS"
|
|
-nographic
|
|
-net nic,model=virtio
|
|
-net "user,hostfwd=tcp::${API_PORT}-:6443,hostfwd=tcp::${SSH_PORT}-:22"
|
|
-drive "file=$DATA_DISK,format=raw,if=virtio"
|
|
-serial "file:$SERIAL_LOG"
|
|
)
|
|
|
|
[ -n "$KVM_FLAG" ] && QEMU_CMD+=("$KVM_FLAG")
|
|
|
|
case "$IMAGE" in
|
|
*.iso)
|
|
QEMU_CMD+=(
|
|
-cdrom "$IMAGE"
|
|
-boot d
|
|
-append "console=${CONSOLE} kubesolo.data=/dev/vda kubesolo.debug $EXTRA_APPEND"
|
|
)
|
|
;;
|
|
*.img)
|
|
QEMU_CMD+=(
|
|
-drive "file=$IMAGE,format=raw,if=virtio"
|
|
)
|
|
;;
|
|
*)
|
|
echo "ERROR: Unrecognized image format: $IMAGE" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
# Launch
|
|
"${QEMU_CMD[@]}" &
|
|
QEMU_PID=$!
|
|
|
|
# Output metadata
|
|
echo "QEMU_PID=$QEMU_PID"
|
|
echo "DATA_DISK=$DATA_DISK"
|
|
echo "SERIAL_LOG=$SERIAL_LOG"
|
|
echo "CREATED_DATA_DISK=$CREATED_DATA_DISK"
|
|
|
|
if [ "$BACKGROUND" = "0" ]; then
|
|
# Foreground mode — wait for QEMU to exit
|
|
wait "$QEMU_PID" || true
|
|
# Clean up temp data disk
|
|
[ -n "$CREATED_DATA_DISK" ] && rm -f "$CREATED_DATA_DISK"
|
|
fi
|