#!/usr/bin/env bash # Boot the Cirros qcow2 under KVM with QMP and a monitor socket exposed. # # This is the v0 VM launcher for phase 2: validate that the orchestrator # and host /proc collector work against a real qemu-system process. No # host-only bridge yet, no exploit driver, no payload — just boot and # idle. We add the bridge and exploit machinery in later phases. # # Run dir is exported so the orchestrator can read the qemu pid: # $RUN_DIR/qemu.pid # $RUN_DIR/qmp.sock # $RUN_DIR/monitor.sock set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" IMAGE="${IMAGE:-$REPO_ROOT/vm/images/alpine-baseline.qcow2}" CIDATA="${CIDATA:-$REPO_ROOT/vm/images/cidata.iso}" # SLOT lets the fleet runner spin up N concurrent VMs without socket / # port collisions. Default RUN_DIR + ssh hostfwd port keep single-VM # usage unchanged. SLOT="${SLOT:-0}" RUN_DIR="${RUN_DIR:-/tmp/cis490-vm-$SLOT}" SSH_PORT="${SSH_PORT:-$((2222 + SLOT))}" # When BRIDGE is set, attach a tap to the host-only bridge instead of # using SLIRP usermode networking. The tap must already exist and be a # member of the bridge — see vm/setup_bridge.sh + (operator) ip tuntap. BRIDGE="${BRIDGE:-}" TAP="${TAP:-cis490tap$SLOT}" mkdir -p "$RUN_DIR" QMP_SOCK="$RUN_DIR/qmp.sock" MON_SOCK="$RUN_DIR/monitor.sock" PID_FILE="$RUN_DIR/qemu.pid" if [[ ! -f "$IMAGE" ]]; then echo "no image at $IMAGE" >&2 exit 1 fi if [[ ! -f "$CIDATA" ]]; then echo "no cidata at $CIDATA — build it with: uv run python tools/build_cidata.py $CIDATA" >&2 exit 1 fi AGENT_SOCK="$RUN_DIR/agent.sock" # snapshot=on routes guest writes through a temporary overlay so the qcow2 # on disk is never mutated — every boot starts from the same bytes. # # Second virtio-serial port (cis490.guest.agent) carries telemetry # from the in-guest agent. Surfaces inside the guest at # /dev/virtio-ports/cis490.guest.agent and on the host at $AGENT_SOCK. exec qemu-system-x86_64 \ -name cis490-vm \ -machine q35,accel=kvm \ -cpu host \ -smp 1,sockets=1,cores=1,threads=1 \ -m 256 \ -drive file="$IMAGE",format=qcow2,if=virtio,snapshot=on \ -drive file="$CIDATA",format=raw,if=virtio,readonly=on \ $(if [[ -n "$BRIDGE" ]]; then \ echo -n "-netdev tap,id=n0,ifname=$TAP,script=no,downscript=no "; \ else \ echo -n "-netdev user,id=n0,hostfwd=tcp:127.0.0.1:$SSH_PORT-:22 "; \ fi) \ -device virtio-net-pci,netdev=n0 \ -device virtio-serial-pci,id=cis490vs0 \ -chardev socket,id=cis490agent,path="$AGENT_SOCK",server=on,wait=off \ -device virtserialport,chardev=cis490agent,name=cis490.guest.agent \ -nographic \ -serial unix:"$RUN_DIR/serial.sock",server=on,wait=off \ -monitor unix:"$MON_SOCK",server=on,wait=off \ -qmp unix:"$QMP_SOCK",server=on,wait=off \ -pidfile "$PID_FILE" \ -display none