diff --git a/build/Dockerfile.builder b/build/Dockerfile.builder index 0465e9b..300ee60 100644 --- a/build/Dockerfile.builder +++ b/build/Dockerfile.builder @@ -42,7 +42,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* # Install Go (for building cloud-init and update agent) -ARG GO_VERSION=1.24.0 +ARG GO_VERSION=1.25.5 RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" \ | tar -C /usr/local -xzf - ENV PATH="/usr/local/go/bin:${PATH}" diff --git a/build/scripts/fetch-components.sh b/build/scripts/fetch-components.sh index dc933c1..c01a2ee 100755 --- a/build/scripts/fetch-components.sh +++ b/build/scripts/fetch-components.sh @@ -98,7 +98,7 @@ else BIN_URL_FALLBACK="https://github.com/portainer/kubesolo/releases/download/${KUBESOLO_VERSION}/kubesolo-${KUBESOLO_VERSION}-${OS}-${ARCH}.tar.gz" TEMP_DIR=$(mktemp -d) - trap "rm -rf '$TEMP_DIR'" EXIT + trap 'rm -rf "$TEMP_DIR"' EXIT echo " URL: $BIN_URL" if curl -fSL "$BIN_URL" -o "$TEMP_DIR/kubesolo.tar.gz" 2>/dev/null; then diff --git a/build/scripts/fetch-rpi-firmware.sh b/build/scripts/fetch-rpi-firmware.sh index 0c03fe1..c3161b8 100755 --- a/build/scripts/fetch-rpi-firmware.sh +++ b/build/scripts/fetch-rpi-firmware.sh @@ -40,7 +40,7 @@ fi # --- Extract boot files only --- echo "==> Extracting boot files..." TEMP_DIR=$(mktemp -d) -trap "rm -rf '$TEMP_DIR'" EXIT +trap 'rm -rf "$TEMP_DIR"' EXIT # Extract only the boot/ directory from the archive # Archive structure: firmware-/boot/... diff --git a/hack/dev-vm-arm64.sh b/hack/dev-vm-arm64.sh index 020beb1..29b2600 100755 --- a/hack/dev-vm-arm64.sh +++ b/hack/dev-vm-arm64.sh @@ -74,7 +74,7 @@ fi DATA_DISK="$(mktemp /tmp/kubesolo-arm64-data-XXXXXX).img" dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null "$MKFS_EXT4" -q -L KSOLODATA "$DATA_DISK" 2>/dev/null -trap "rm -f '$DATA_DISK'" EXIT +trap 'rm -f "$DATA_DISK"' EXIT echo "==> Launching ARM64 QEMU VM..." echo " Kernel: $VMLINUZ" @@ -96,5 +96,5 @@ qemu-system-aarch64 \ -initrd "$INITRD" \ -append "console=ttyAMA0 kubesolo.data=/dev/vda kubesolo.debug $EXTRA_APPEND" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \ - -net nic,model=virtio \ + -net "nic,model=virtio" \ -net "user,hostfwd=tcp::6443-:6443,hostfwd=tcp::2222-:22" diff --git a/hack/dev-vm.sh b/hack/dev-vm.sh index 78fe79c..4a010bd 100755 --- a/hack/dev-vm.sh +++ b/hack/dev-vm.sh @@ -85,8 +85,8 @@ trap cleanup EXIT # Build QEMU command QEMU_ARGS=(-m 2048 -smp 2 -nographic -cpu max) -QEMU_ARGS+=(-net nic,model=virtio) -QEMU_ARGS+=(-net user,hostfwd=tcp::6443-:6443,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:8080) +QEMU_ARGS+=(-net "nic,model=virtio") +QEMU_ARGS+=(-net "user,hostfwd=tcp::6443-:6443,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:8080") if [ -n "$DATA_DISK" ]; then QEMU_ARGS+=(-drive "file=$DATA_DISK,format=raw,if=virtio") diff --git a/test/integration/test-security-hardening.sh b/test/integration/test-security-hardening.sh index 89fcab8..9df7a9d 100755 --- a/test/integration/test-security-hardening.sh +++ b/test/integration/test-security-hardening.sh @@ -21,9 +21,11 @@ dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null QEMU_PID="" +EXTRACT_DIR="" cleanup() { [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true rm -f "$DATA_DISK" "$SERIAL_LOG" + [ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR" } trap cleanup EXIT @@ -35,18 +37,77 @@ echo " Serial log: $SERIAL_LOG" KVM_FLAG="" [ -w /dev/kvm ] 2>/dev/null && KVM_FLAG="-enable-kvm" -# Launch QEMU in background +# Extract kernel + initramfs from ISO (direct kernel boot required for -append) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}" +EXTRACT_DIR="" + +VMLINUZ="" +INITRAMFS="" + +if [ -f "$ROOTFS_DIR/vmlinuz" ] && [ -f "$ROOTFS_DIR/kubesolo-os.gz" ]; then + VMLINUZ="$ROOTFS_DIR/vmlinuz" + INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz" + echo " Using kernel/initramfs from build directory" +else + EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)" + EXTRACTED=0 + + echo " Extracting kernel/initramfs from ISO..." + + if [ $EXTRACTED -eq 0 ] && command -v bsdtar >/dev/null 2>&1; then + if bsdtar -xf "$ISO" -C "$EXTRACT_DIR" boot/vmlinuz boot/kubesolo-os.gz 2>/dev/null; then + EXTRACTED=1 + fi + fi + + if [ $EXTRACTED -eq 0 ] && command -v isoinfo >/dev/null 2>&1; then + mkdir -p "$EXTRACT_DIR/boot" + isoinfo -i "$ISO" -x "/BOOT/VMLINUZ;1" > "$EXTRACT_DIR/boot/vmlinuz" 2>/dev/null || true + isoinfo -i "$ISO" -x "/BOOT/KUBESOLO-OS.GZ;1" > "$EXTRACT_DIR/boot/kubesolo-os.gz" 2>/dev/null || true + if [ -s "$EXTRACT_DIR/boot/vmlinuz" ] && [ -s "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then + EXTRACTED=1 + else + rm -f "$EXTRACT_DIR/boot/vmlinuz" "$EXTRACT_DIR/boot/kubesolo-os.gz" + fi + fi + + if [ $EXTRACTED -eq 0 ] && [ "$(uname)" = "Linux" ]; then + ISO_MOUNT="$EXTRACT_DIR/mnt" + mkdir -p "$ISO_MOUNT" + if mount -o loop,ro "$ISO" "$ISO_MOUNT" 2>/dev/null; then + mkdir -p "$EXTRACT_DIR/boot" + cp "$ISO_MOUNT/boot/vmlinuz" "$EXTRACT_DIR/boot/" 2>/dev/null || true + cp "$ISO_MOUNT/boot/kubesolo-os.gz" "$EXTRACT_DIR/boot/" 2>/dev/null || true + umount "$ISO_MOUNT" 2>/dev/null || true + if [ -f "$EXTRACT_DIR/boot/vmlinuz" ] && [ -f "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then + EXTRACTED=1 + fi + fi + fi + + if [ $EXTRACTED -eq 0 ]; then + echo "ERROR: Failed to extract kernel/initramfs from ISO." + exit 1 + fi + + VMLINUZ="$EXTRACT_DIR/boot/vmlinuz" + INITRAMFS="$EXTRACT_DIR/boot/kubesolo-os.gz" +fi + +# Launch QEMU in background with direct kernel boot # shellcheck disable=SC2086 qemu-system-x86_64 \ -m 2048 -smp 2 \ -nographic \ $KVM_FLAG \ - -cdrom "$ISO" \ - -boot d \ + -kernel "$VMLINUZ" \ + -initrd "$INITRAMFS" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \ - -net nic,model=virtio \ + -net "nic,model=virtio" \ -net "user,hostfwd=tcp::18080-:8080" \ - -serial file:"$SERIAL_LOG" \ + -serial "file:$SERIAL_LOG" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda kubesolo.debug" \ & QEMU_PID=$! @@ -56,7 +117,7 @@ echo " Waiting for boot..." ELAPSED=0 BOOTED=0 while [ "$ELAPSED" -lt "$TIMEOUT_BOOT" ]; do - if grep -q "\[kubesolo-init\] \[OK\] Stage 90-kubesolo.sh complete" "$SERIAL_LOG" 2>/dev/null; then + if grep -q "\[kubesolo-init\] \[OK\] KubeSolo is running" "$SERIAL_LOG" 2>/dev/null; then BOOTED=1 break fi diff --git a/test/qemu/run-vm.sh b/test/qemu/run-vm.sh index 8b04431..f31bf35 100755 --- a/test/qemu/run-vm.sh +++ b/test/qemu/run-vm.sh @@ -91,7 +91,7 @@ if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then -m "$MEMORY" -smp "$CPUS" -nographic - -net nic,model=virtio + -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" @@ -114,7 +114,7 @@ else -m "$MEMORY" -smp "$CPUS" -nographic - -net nic,model=virtio + -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" diff --git a/test/qemu/test-boot.sh b/test/qemu/test-boot.sh index 249e32a..2caba2f 100755 --- a/test/qemu/test-boot.sh +++ b/test/qemu/test-boot.sh @@ -5,7 +5,7 @@ set -euo pipefail ISO="${1:?Usage: $0 }" -TIMEOUT_BOOT=120 # seconds to wait for boot success marker +TIMEOUT_BOOT=${TIMEOUT_BOOT:-120} # seconds to wait for boot success marker SERIAL_LOG=$(mktemp /tmp/kubesolo-boot-test-XXXXXX.log) # Temp data disk @@ -13,9 +13,13 @@ DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) dd if=/dev/zero of="$DATA_DISK" bs=1M count=512 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null +EXTRACT_DIR="" +QEMU_PID="" + cleanup() { - kill "$QEMU_PID" 2>/dev/null || true + [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true rm -f "$DATA_DISK" "$SERIAL_LOG" + [ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR" } trap cleanup EXIT @@ -23,16 +27,88 @@ echo "==> Boot test: $ISO" echo " Timeout: ${TIMEOUT_BOOT}s" echo " Serial log: $SERIAL_LOG" -# Launch QEMU in background +# Extract kernel + initramfs from ISO (direct kernel boot required for -append) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}" + +VMLINUZ="" +INITRAMFS="" + +if [ -f "$ROOTFS_DIR/vmlinuz" ] && [ -f "$ROOTFS_DIR/kubesolo-os.gz" ]; then + VMLINUZ="$ROOTFS_DIR/vmlinuz" + INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz" + echo " Using kernel/initramfs from build directory" +else + EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)" + EXTRACTED=0 + + echo " Extracting kernel/initramfs from ISO..." + + # Method 1: bsdtar + if [ $EXTRACTED -eq 0 ] && command -v bsdtar >/dev/null 2>&1; then + if bsdtar -xf "$ISO" -C "$EXTRACT_DIR" boot/vmlinuz boot/kubesolo-os.gz 2>/dev/null; then + echo " Extracted via bsdtar" + EXTRACTED=1 + fi + fi + + # Method 2: isoinfo + if [ $EXTRACTED -eq 0 ] && command -v isoinfo >/dev/null 2>&1; then + mkdir -p "$EXTRACT_DIR/boot" + isoinfo -i "$ISO" -x "/BOOT/VMLINUZ;1" > "$EXTRACT_DIR/boot/vmlinuz" 2>/dev/null || true + isoinfo -i "$ISO" -x "/BOOT/KUBESOLO-OS.GZ;1" > "$EXTRACT_DIR/boot/kubesolo-os.gz" 2>/dev/null || true + if [ -s "$EXTRACT_DIR/boot/vmlinuz" ] && [ -s "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then + echo " Extracted via isoinfo" + EXTRACTED=1 + else + rm -f "$EXTRACT_DIR/boot/vmlinuz" "$EXTRACT_DIR/boot/kubesolo-os.gz" + fi + fi + + # Method 3: loop mount (Linux only) + if [ $EXTRACTED -eq 0 ] && [ "$(uname)" = "Linux" ]; then + ISO_MOUNT="$EXTRACT_DIR/mnt" + mkdir -p "$ISO_MOUNT" + if mount -o loop,ro "$ISO" "$ISO_MOUNT" 2>/dev/null; then + mkdir -p "$EXTRACT_DIR/boot" + cp "$ISO_MOUNT/boot/vmlinuz" "$EXTRACT_DIR/boot/" 2>/dev/null || true + cp "$ISO_MOUNT/boot/kubesolo-os.gz" "$EXTRACT_DIR/boot/" 2>/dev/null || true + umount "$ISO_MOUNT" 2>/dev/null || true + if [ -f "$EXTRACT_DIR/boot/vmlinuz" ] && [ -f "$EXTRACT_DIR/boot/kubesolo-os.gz" ]; then + echo " Extracted via loop mount" + EXTRACTED=1 + fi + fi + fi + + if [ $EXTRACTED -eq 0 ]; then + echo "ERROR: Failed to extract kernel/initramfs from ISO." + exit 1 + fi + + VMLINUZ="$EXTRACT_DIR/boot/vmlinuz" + INITRAMFS="$EXTRACT_DIR/boot/kubesolo-os.gz" +fi + +# Detect KVM +KVM_FLAG="" +if [ -w /dev/kvm ] 2>/dev/null; then + KVM_FLAG="-enable-kvm" + echo " KVM acceleration: enabled" +fi + +# Launch QEMU in background with direct kernel boot qemu-system-x86_64 \ -m 2048 -smp 2 \ -nographic \ - -cdrom "$ISO" \ - -boot d \ + $KVM_FLAG \ + -kernel "$VMLINUZ" \ + -initrd "$INITRAMFS" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \ - -net nic,model=virtio \ + -net "nic,model=virtio" \ -net user \ - -serial file:"$SERIAL_LOG" \ + -serial "file:$SERIAL_LOG" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda kubesolo.debug" \ & QEMU_PID=$! @@ -41,7 +117,7 @@ QEMU_PID=$! echo " Waiting for boot..." ELAPSED=0 while [ "$ELAPSED" -lt "$TIMEOUT_BOOT" ]; do - if grep -q "\[kubesolo-init\] \[OK\] Stage 90-kubesolo.sh complete" "$SERIAL_LOG" 2>/dev/null; then + if grep -q "\[kubesolo-init\] \[OK\] KubeSolo is running" "$SERIAL_LOG" 2>/dev/null; then echo "" echo "==> PASS: KubeSolo OS booted successfully in ${ELAPSED}s" exit 0