fix: kernel AppArmor 2-pass olddefconfig and QEMU test direct kernel boot
Some checks failed
CI / Go Tests (push) Has been cancelled
CI / Build Go Binaries (amd64, linux, linux-amd64) (push) Has been cancelled
CI / Build Go Binaries (arm64, linux, linux-arm64) (push) Has been cancelled
CI / Shellcheck (push) Has been cancelled

The stock TinyCore kernel config has "# CONFIG_SECURITY is not set" which
caused make olddefconfig to silently revert all security configs in a single
pass. Fix by applying security configs (AppArmor, Audit, LSM) after the
first olddefconfig resolves base dependencies, then running a second pass.
Added mandatory verification that exits on missing critical configs.

All QEMU test scripts converted from broken -cdrom + -append pattern to
direct kernel boot (-kernel + -initrd) via shared test/lib/qemu-helpers.sh
helper library. The -append flag only works with -kernel, not -cdrom.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 14:11:38 -06:00
parent 958524e6d8
commit 6c15ba7776
9 changed files with 261 additions and 187 deletions

View File

@@ -96,17 +96,6 @@ echo "==> Enabling required kernel configs..."
./scripts/config --enable CONFIG_MEMCG ./scripts/config --enable CONFIG_MEMCG
./scripts/config --enable CONFIG_CFS_BANDWIDTH ./scripts/config --enable CONFIG_CFS_BANDWIDTH
# Security: AppArmor LSM + Audit subsystem
echo "==> Enabling AppArmor + Audit kernel configs..."
./scripts/config --enable CONFIG_AUDIT
./scripts/config --enable CONFIG_AUDITSYSCALL
./scripts/config --enable CONFIG_SECURITY
./scripts/config --enable CONFIG_SECURITYFS
./scripts/config --enable CONFIG_SECURITY_NETWORK
./scripts/config --enable CONFIG_SECURITY_APPARMOR
./scripts/config --set-str CONFIG_LSM "lockdown,yama,apparmor"
./scripts/config --set-str CONFIG_DEFAULT_SECURITY "apparmor"
# --- Strip unnecessary subsystems for smallest footprint --- # --- Strip unnecessary subsystems for smallest footprint ---
# This is a headless K8s edge appliance — no sound, GPU, wireless, etc. # This is a headless K8s edge appliance — no sound, GPU, wireless, etc.
echo "==> Disabling unnecessary subsystems for minimal footprint..." echo "==> Disabling unnecessary subsystems for minimal footprint..."
@@ -156,13 +145,29 @@ echo "==> Disabling unnecessary subsystems for minimal footprint..."
# FPGA (not needed) # FPGA (not needed)
./scripts/config --disable FPGA ./scripts/config --disable FPGA
# Resolve dependencies (olddefconfig accepts defaults for new options) # First pass: resolve base dependencies before adding security configs.
# The stock TC config has "# CONFIG_SECURITY is not set" which causes
# olddefconfig to strip security-related options if applied in a single pass.
make olddefconfig make olddefconfig
# Verify CONFIG_CGROUP_BPF is set # Security: AppArmor LSM + Audit subsystem
if grep -q 'CONFIG_CGROUP_BPF=y' .config; then # Applied AFTER first olddefconfig to ensure CONFIG_SECURITY dependencies
echo " CONFIG_CGROUP_BPF=y confirmed in .config" # (SYSFS, MULTIUSER) are resolved before enabling the security subtree.
else echo "==> Enabling AppArmor + Audit kernel configs..."
./scripts/config --enable CONFIG_AUDIT
./scripts/config --enable CONFIG_AUDITSYSCALL
./scripts/config --enable CONFIG_SECURITY
./scripts/config --enable CONFIG_SECURITYFS
./scripts/config --enable CONFIG_SECURITY_NETWORK
./scripts/config --enable CONFIG_SECURITY_APPARMOR
./scripts/config --set-str CONFIG_LSM "lockdown,yama,apparmor"
./scripts/config --set-str CONFIG_DEFAULT_SECURITY "apparmor"
# Second pass: resolve security config dependencies
make olddefconfig
# Verify critical configs are set
if ! grep -q 'CONFIG_CGROUP_BPF=y' .config; then
echo "ERROR: CONFIG_CGROUP_BPF not set after olddefconfig" echo "ERROR: CONFIG_CGROUP_BPF not set after olddefconfig"
grep 'CGROUP_BPF' .config || echo " (CGROUP_BPF not found in .config)" grep 'CGROUP_BPF' .config || echo " (CGROUP_BPF not found in .config)"
echo "" echo ""
@@ -170,10 +175,25 @@ else
grep -E 'CONFIG_BPF=|CONFIG_BPF_SYSCALL=' .config || echo " BPF not found" grep -E 'CONFIG_BPF=|CONFIG_BPF_SYSCALL=' .config || echo " BPF not found"
exit 1 exit 1
fi fi
echo " CONFIG_CGROUP_BPF=y confirmed"
# Show what changed if ! grep -q 'CONFIG_SECURITY_APPARMOR=y' .config; then
echo " Config diff from stock:" echo "ERROR: CONFIG_SECURITY_APPARMOR not set after olddefconfig"
diff "$KERNEL_CFG" .config | grep '^[<>]' | head -20 || echo " (no differences beyond CGROUP_BPF)" echo " Security-related configs:"
grep -E 'CONFIG_SECURITY=|CONFIG_SECURITYFS=|CONFIG_SECURITY_APPARMOR=' .config
exit 1
fi
echo " CONFIG_SECURITY_APPARMOR=y confirmed"
if ! grep -q 'CONFIG_AUDIT=y' .config; then
echo "ERROR: CONFIG_AUDIT not set after olddefconfig"
exit 1
fi
echo " CONFIG_AUDIT=y confirmed"
# Show what changed (security-related)
echo " Key config values:"
grep -E 'CONFIG_SECURITY=|CONFIG_SECURITY_APPARMOR=|CONFIG_AUDIT=|CONFIG_LSM=|CONFIG_CGROUP_BPF=' .config | sed 's/^/ /'
# --- Build kernel + modules --- # --- Build kernel + modules ---
NPROC=$(nproc 2>/dev/null || echo 4) NPROC=$(nproc 2>/dev/null || echo 4)

View File

@@ -22,6 +22,8 @@ RUNS=3
SSH_PORT=2222 SSH_PORT=2222
K8S_PORT=6443 K8S_PORT=6443
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
shift || true shift || true
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case "$1" in
@@ -47,6 +49,15 @@ echo "Type: $IMAGE_TYPE" >&2
echo "Runs: $RUNS" >&2 echo "Runs: $RUNS" >&2
echo "" >&2 echo "" >&2
EXTRACT_DIR=""
TEMP_DISK=""
cleanup() {
[ -n "$TEMP_DISK" ] && rm -f "$TEMP_DISK"
[ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
}
trap cleanup EXIT
# Build QEMU command # Build QEMU command
QEMU_CMD=( QEMU_CMD=(
qemu-system-x86_64 qemu-system-x86_64
@@ -55,24 +66,31 @@ QEMU_CMD=(
-nographic -nographic
-no-reboot -no-reboot
-serial mon:stdio -serial mon:stdio
-net nic,model=virtio -net "nic,model=virtio"
-net "user,hostfwd=tcp::${SSH_PORT}-:22,hostfwd=tcp::${K8S_PORT}-:6443" -net "user,hostfwd=tcp::${SSH_PORT}-:22,hostfwd=tcp::${K8S_PORT}-:6443"
) )
# Add KVM if available # Add KVM if available
if [ -e /dev/kvm ] && [ -r /dev/kvm ]; then KVM_FLAG=$(detect_kvm)
if [ -n "$KVM_FLAG" ]; then
QEMU_CMD+=(-enable-kvm -cpu host) QEMU_CMD+=(-enable-kvm -cpu host)
echo "KVM: enabled" >&2
else else
QEMU_CMD+=(-cpu max) QEMU_CMD+=(-cpu max)
echo "KVM: not available (TCG)" >&2
fi fi
echo "" >&2
if [ "$IMAGE_TYPE" = "iso" ]; then if [ "$IMAGE_TYPE" = "iso" ]; then
QEMU_CMD+=(-cdrom "$IMAGE") # Extract kernel/initramfs for direct boot (required for -append to work)
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-bench-extract-XXXXXX)"
extract_kernel_from_iso "$IMAGE" "$EXTRACT_DIR" >&2
QEMU_CMD+=(-kernel "$VMLINUZ" -initrd "$INITRAMFS")
QEMU_CMD+=(-append "console=ttyS0,115200n8 kubesolo.debug")
# Add a temp disk for persistence # Add a temp disk for persistence
TEMP_DISK=$(mktemp /tmp/kubesolo-bench-XXXXXX.img) TEMP_DISK=$(mktemp /tmp/kubesolo-bench-XXXXXX.img)
qemu-img create -f qcow2 "$TEMP_DISK" 8G >/dev/null 2>&1 qemu-img create -f qcow2 "$TEMP_DISK" 8G >/dev/null 2>&1
QEMU_CMD+=(-drive "file=$TEMP_DISK,format=qcow2,if=virtio") QEMU_CMD+=(-drive "file=$TEMP_DISK,format=qcow2,if=virtio")
trap "rm -f $TEMP_DISK" EXIT
else else
QEMU_CMD+=(-drive "file=$IMAGE,format=raw,if=virtio") QEMU_CMD+=(-drive "file=$IMAGE,format=raw,if=virtio")
fi fi
@@ -111,7 +129,7 @@ for run in $(seq 1 "$RUNS"); do
echo "KERNEL_MS=$ELAPSED_MS" >> "$LOG.times" echo "KERNEL_MS=$ELAPSED_MS" >> "$LOG.times"
fi fi
;; ;;
*"kubesolo-init"*"all stages complete"*|*"init complete"*) *"KubeSolo is running"*|*"kubesolo-init"*"OK"*)
if [ -z "$INIT_DONE" ]; then if [ -z "$INIT_DONE" ]; then
INIT_DONE="$ELAPSED_MS" INIT_DONE="$ELAPSED_MS"
echo " Init complete: ${ELAPSED_MS}ms" >&2 echo " Init complete: ${ELAPSED_MS}ms" >&2

View File

@@ -5,19 +5,26 @@
set -euo pipefail set -euo pipefail
ISO="${1:?Usage: $0 <path-to-iso>}" ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_BOOT=120 TIMEOUT_K8S=${TIMEOUT_K8S:-300}
TIMEOUT_K8S=300 TIMEOUT_POD=${TIMEOUT_POD:-120}
TIMEOUT_POD=120
API_PORT=6443 API_PORT=6443
SERIAL_LOG=$(mktemp /tmp/kubesolo-workload-XXXXXX.log) SERIAL_LOG=$(mktemp /tmp/kubesolo-workload-XXXXXX.log)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
QEMU_PID=""
EXTRACT_DIR=""
cleanup() { cleanup() {
kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
rm -f "$DATA_DISK" "$SERIAL_LOG" rm -f "$DATA_DISK" "$SERIAL_LOG"
[ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
} }
trap cleanup EXIT trap cleanup EXIT
@@ -25,14 +32,22 @@ KUBECTL="kubectl --server=https://localhost:${API_PORT} --insecure-skip-tls-veri
echo "==> Workload deployment test: $ISO" echo "==> Workload deployment test: $ISO"
# Extract kernel from ISO
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
KVM_FLAG=$(detect_kvm)
# Launch QEMU # Launch QEMU
# shellcheck disable=SC2086
qemu-system-x86_64 \ qemu-system-x86_64 \
-m 2048 -smp 2 \ -m 2048 -smp 2 \
-nographic \ -nographic \
-cdrom "$ISO" \ $KVM_FLAG \
-boot d \ -kernel "$VMLINUZ" \
-initrd "$INITRAMFS" \
-drive "file=$DATA_DISK,format=raw,if=virtio" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \
-net nic,model=virtio \ -net "nic,model=virtio" \
-net "user,hostfwd=tcp::${API_PORT}-:6443" \ -net "user,hostfwd=tcp::${API_PORT}-:6443" \
-serial "file:$SERIAL_LOG" \ -serial "file:$SERIAL_LOG" \
-append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \
@@ -71,6 +86,7 @@ $KUBECTL run test-nginx --image=nginx:alpine --restart=Never 2>/dev/null || {
echo " Waiting for pod to reach Running..." echo " Waiting for pod to reach Running..."
ELAPSED=0 ELAPSED=0
POD_RUNNING=0 POD_RUNNING=0
STATUS=""
while [ "$ELAPSED" -lt "$TIMEOUT_POD" ]; do while [ "$ELAPSED" -lt "$TIMEOUT_POD" ]; do
STATUS=$($KUBECTL get pod test-nginx -o jsonpath='{.status.phase}' 2>/dev/null || echo "") STATUS=$($KUBECTL get pod test-nginx -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
if [ "$STATUS" = "Running" ]; then if [ "$STATUS" = "Running" ]; then

View File

@@ -5,31 +5,47 @@
set -euo pipefail set -euo pipefail
ISO="${1:?Usage: $0 <path-to-iso>}" ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_BOOT=120 TIMEOUT_K8S=${TIMEOUT_K8S:-300}
TIMEOUT_K8S=300
API_PORT=6443 API_PORT=6443
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
QEMU_PID=""
EXTRACT_DIR=""
cleanup() { cleanup() {
kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
rm -f "$DATA_DISK" rm -f "$DATA_DISK"
[ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
} }
trap cleanup EXIT trap cleanup EXIT
echo "==> K8s readiness test: $ISO" echo "==> K8s readiness test: $ISO"
# Extract kernel from ISO
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
KVM_FLAG=$(detect_kvm)
[ -n "$KVM_FLAG" ] && echo " KVM acceleration: enabled"
# Launch QEMU with API port forwarded # Launch QEMU with API port forwarded
# shellcheck disable=SC2086
qemu-system-x86_64 \ qemu-system-x86_64 \
-m 2048 -smp 2 \ -m 2048 -smp 2 \
-nographic \ -nographic \
-cdrom "$ISO" \ $KVM_FLAG \
-boot d \ -kernel "$VMLINUZ" \
-initrd "$INITRAMFS" \
-drive "file=$DATA_DISK,format=raw,if=virtio" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \
-net nic,model=virtio \ -net "nic,model=virtio" \
-net user,hostfwd=tcp::${API_PORT}-:6443 \ -net "user,hostfwd=tcp::${API_PORT}-:6443" \
-append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \
& &
QEMU_PID=$! QEMU_PID=$!

View File

@@ -5,37 +5,52 @@
set -euo pipefail set -euo pipefail
ISO="${1:?Usage: $0 <path-to-iso>}" ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_K8S=300 TIMEOUT_K8S=${TIMEOUT_K8S:-300}
TIMEOUT_PVC=120 TIMEOUT_PVC=${TIMEOUT_PVC:-120}
API_PORT=6443 API_PORT=6443
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=2048 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=2048 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
SERIAL_LOG=$(mktemp /tmp/kubesolo-storage-XXXXXX.log) SERIAL_LOG=$(mktemp /tmp/kubesolo-storage-XXXXXX.log)
QEMU_PID=""
EXTRACT_DIR=""
KUBECTL="kubectl --server=https://localhost:${API_PORT} --insecure-skip-tls-verify"
cleanup() { cleanup() {
# Clean up K8s resources # Clean up K8s resources
$KUBECTL delete pod test-storage --grace-period=0 --force 2>/dev/null || true $KUBECTL delete pod test-storage --grace-period=0 --force 2>/dev/null || true
$KUBECTL delete pvc test-pvc 2>/dev/null || true $KUBECTL delete pvc test-pvc 2>/dev/null || true
kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
rm -f "$DATA_DISK" "$SERIAL_LOG" rm -f "$DATA_DISK" "$SERIAL_LOG"
[ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
} }
trap cleanup EXIT trap cleanup EXIT
KUBECTL="kubectl --server=https://localhost:${API_PORT} --insecure-skip-tls-verify"
echo "==> Local storage test: $ISO" echo "==> Local storage test: $ISO"
# Extract kernel from ISO
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
KVM_FLAG=$(detect_kvm)
# Launch QEMU # Launch QEMU
# shellcheck disable=SC2086
qemu-system-x86_64 \ qemu-system-x86_64 \
-m 2048 -smp 2 \ -m 2048 -smp 2 \
-nographic \ -nographic \
-cdrom "$ISO" \ $KVM_FLAG \
-boot d \ -kernel "$VMLINUZ" \
-initrd "$INITRAMFS" \
-drive "file=$DATA_DISK,format=raw,if=virtio" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \
-net nic,model=virtio \ -net "nic,model=virtio" \
-net "user,hostfwd=tcp::${API_PORT}-:6443" \ -net "user,hostfwd=tcp::${API_PORT}-:6443" \
-serial "file:$SERIAL_LOG" \ -serial "file:$SERIAL_LOG" \
-append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \
@@ -98,6 +113,7 @@ YAML
# Wait for pod Running # Wait for pod Running
echo " Waiting for storage pod..." echo " Waiting for storage pod..."
ELAPSED=0 ELAPSED=0
STATUS=""
while [ "$ELAPSED" -lt "$TIMEOUT_PVC" ]; do while [ "$ELAPSED" -lt "$TIMEOUT_PVC" ]; do
STATUS=$($KUBECTL get pod test-storage -o jsonpath='{.status.phase}' 2>/dev/null || echo "") STATUS=$($KUBECTL get pod test-storage -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
if [ "$STATUS" = "Running" ]; then if [ "$STATUS" = "Running" ]; then

View File

@@ -6,35 +6,50 @@
set -euo pipefail set -euo pipefail
ISO="${1:?Usage: $0 <path-to-iso>}" ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_K8S=300 TIMEOUT_K8S=${TIMEOUT_K8S:-300}
TIMEOUT_POD=120 TIMEOUT_POD=${TIMEOUT_POD:-120}
API_PORT=6443 API_PORT=6443
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
SERIAL_LOG=$(mktemp /tmp/kubesolo-netpol-XXXXXX.log) SERIAL_LOG=$(mktemp /tmp/kubesolo-netpol-XXXXXX.log)
QEMU_PID=""
EXTRACT_DIR=""
KUBECTL="kubectl --server=https://localhost:${API_PORT} --insecure-skip-tls-verify"
cleanup() { cleanup() {
$KUBECTL delete namespace netpol-test 2>/dev/null || true $KUBECTL delete namespace netpol-test 2>/dev/null || true
kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
rm -f "$DATA_DISK" "$SERIAL_LOG" rm -f "$DATA_DISK" "$SERIAL_LOG"
[ -n "$EXTRACT_DIR" ] && rm -rf "$EXTRACT_DIR"
} }
trap cleanup EXIT trap cleanup EXIT
KUBECTL="kubectl --server=https://localhost:${API_PORT} --insecure-skip-tls-verify"
echo "==> Network policy test: $ISO" echo "==> Network policy test: $ISO"
# Extract kernel from ISO
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
KVM_FLAG=$(detect_kvm)
# Launch QEMU # Launch QEMU
# shellcheck disable=SC2086
qemu-system-x86_64 \ qemu-system-x86_64 \
-m 2048 -smp 2 \ -m 2048 -smp 2 \
-nographic \ -nographic \
-cdrom "$ISO" \ $KVM_FLAG \
-boot d \ -kernel "$VMLINUZ" \
-initrd "$INITRAMFS" \
-drive "file=$DATA_DISK,format=raw,if=virtio" \ -drive "file=$DATA_DISK,format=raw,if=virtio" \
-net nic,model=virtio \ -net "nic,model=virtio" \
-net "user,hostfwd=tcp::${API_PORT}-:6443" \ -net "user,hostfwd=tcp::${API_PORT}-:6443" \
-serial "file:$SERIAL_LOG" \ -serial "file:$SERIAL_LOG" \
-append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \ -append "console=ttyS0,115200n8 kubesolo.data=/dev/vda" \
@@ -81,6 +96,7 @@ YAML
# Wait for pod # Wait for pod
ELAPSED=0 ELAPSED=0
STATUS=""
while [ "$ELAPSED" -lt "$TIMEOUT_POD" ]; do while [ "$ELAPSED" -lt "$TIMEOUT_POD" ]; do
STATUS=$($KUBECTL get pod -n netpol-test web -o jsonpath='{.status.phase}' 2>/dev/null || echo "") STATUS=$($KUBECTL get pod -n netpol-test web -o jsonpath='{.status.phase}' 2>/dev/null || echo "")
[ "$STATUS" = "Running" ] && break [ "$STATUS" = "Running" ] && break

View File

@@ -12,9 +12,13 @@
set -euo pipefail set -euo pipefail
ISO="${1:?Usage: $0 <path-to-iso>}" ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_BOOT=180 # seconds to wait for boot TIMEOUT_BOOT=${TIMEOUT_BOOT:-180} # seconds to wait for boot
SERIAL_LOG=$(mktemp /tmp/kubesolo-security-test-XXXXXX.log) SERIAL_LOG=$(mktemp /tmp/kubesolo-security-test-XXXXXX.log)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
# Temp data disk # Temp data disk
DATA_DISK=$(mktemp /tmp/kubesolo-security-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-security-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=1024 2>/dev/null
@@ -22,6 +26,7 @@ mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
QEMU_PID="" QEMU_PID=""
EXTRACT_DIR="" EXTRACT_DIR=""
cleanup() { cleanup() {
[ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
rm -f "$DATA_DISK" "$SERIAL_LOG" rm -f "$DATA_DISK" "$SERIAL_LOG"
@@ -33,68 +38,12 @@ echo "==> Security Hardening Test: $ISO"
echo " Timeout: ${TIMEOUT_BOOT}s" echo " Timeout: ${TIMEOUT_BOOT}s"
echo " Serial log: $SERIAL_LOG" echo " Serial log: $SERIAL_LOG"
# Extract kernel from ISO
EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
# Detect KVM # Detect KVM
KVM_FLAG="" KVM_FLAG=$(detect_kvm)
[ -w /dev/kvm ] 2>/dev/null && KVM_FLAG="-enable-kvm"
# 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 # Launch QEMU in background with direct kernel boot
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@@ -171,7 +120,7 @@ fi
echo "" echo ""
echo "--- Test 2: AppArmor ---" echo "--- Test 2: AppArmor ---"
if grep -q "AppArmor profiles loaded" "$SERIAL_LOG" 2>/dev/null; then if grep -q "AppArmor.*loaded.*profiles" "$SERIAL_LOG" 2>/dev/null; then
check_pass "AppArmor profiles loaded" check_pass "AppArmor profiles loaded"
elif grep -q "AppArmor not available" "$SERIAL_LOG" 2>/dev/null; then elif grep -q "AppArmor not available" "$SERIAL_LOG" 2>/dev/null; then
check_skip "AppArmor not in kernel (expected before kernel rebuild)" check_skip "AppArmor not in kernel (expected before kernel rebuild)"

82
test/lib/qemu-helpers.sh Normal file
View File

@@ -0,0 +1,82 @@
#!/bin/bash
# qemu-helpers.sh — Shared functions for QEMU-based tests
# Source this file from test scripts: . "$(dirname "$0")/../lib/qemu-helpers.sh"
# extract_kernel_from_iso <iso-path> <extract-dir>
# Sets VMLINUZ and INITRAMFS variables on success
# Falls back to build/rootfs-work/ if available
extract_kernel_from_iso() {
local iso="$1"
local extract_dir="$2"
local project_root="${PROJECT_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
local rootfs_dir="${ROOTFS_DIR:-$project_root/build/rootfs-work}"
VMLINUZ=""
INITRAMFS=""
# Prefer build artifacts (no extraction needed)
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"
return 0
fi
local extracted=0
echo " Extracting kernel/initramfs from ISO..."
# Method 1: bsdtar (ships with macOS, libarchive-tools on Linux)
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 (genisoimage/cdrtools)
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, may need root)
if [ $extracted -eq 0 ] && [ "$(uname)" = "Linux" ]; then
local 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."
echo " Install one of: bsdtar (libarchive-tools), isoinfo (genisoimage), or run as root for loop mount."
return 1
fi
VMLINUZ="$extract_dir/boot/vmlinuz"
INITRAMFS="$extract_dir/boot/kubesolo-os.gz"
return 0
}
# detect_kvm — prints "-enable-kvm" if KVM available, empty string otherwise
detect_kvm() {
if [ -w /dev/kvm ] 2>/dev/null; then
echo "-enable-kvm"
fi
}

View File

@@ -8,13 +8,17 @@ ISO="${1:?Usage: $0 <path-to-iso>}"
TIMEOUT_BOOT=${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) SERIAL_LOG=$(mktemp /tmp/kubesolo-boot-test-XXXXXX.log)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
. "$SCRIPT_DIR/../lib/qemu-helpers.sh"
# Temp data disk # Temp data disk
DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img) DATA_DISK=$(mktemp /tmp/kubesolo-data-XXXXXX.img)
dd if=/dev/zero of="$DATA_DISK" bs=1M count=512 2>/dev/null dd if=/dev/zero of="$DATA_DISK" bs=1M count=512 2>/dev/null
mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null mkfs.ext4 -q -L KSOLODATA "$DATA_DISK" 2>/dev/null
EXTRACT_DIR=""
QEMU_PID="" QEMU_PID=""
EXTRACT_DIR=""
cleanup() { cleanup() {
[ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
@@ -27,78 +31,15 @@ echo "==> Boot test: $ISO"
echo " Timeout: ${TIMEOUT_BOOT}s" echo " Timeout: ${TIMEOUT_BOOT}s"
echo " Serial log: $SERIAL_LOG" echo " Serial log: $SERIAL_LOG"
# Extract kernel + initramfs from ISO (direct kernel boot required for -append) # Extract kernel from ISO
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" EXTRACT_DIR="$(mktemp -d /tmp/kubesolo-extract-XXXXXX)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" extract_kernel_from_iso "$ISO" "$EXTRACT_DIR"
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
VMLINUZ="" KVM_FLAG=$(detect_kvm)
INITRAMFS="" [ -n "$KVM_FLAG" ] && echo " KVM acceleration: enabled"
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 # Launch QEMU in background with direct kernel boot
# shellcheck disable=SC2086
qemu-system-x86_64 \ qemu-system-x86_64 \
-m 2048 -smp 2 \ -m 2048 -smp 2 \
-nographic \ -nographic \