#!/usr/bin/env bash set -euo pipefail log() { printf '[verify] %s\n' "$*" } DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEFAULT_RUNTIME_DIR="$DIR" if [[ -d "$DIR/runtime" ]]; then DEFAULT_RUNTIME_DIR="$DIR/runtime" fi RUNTIME_DIR="${BANGER_RUNTIME_DIR:-$DEFAULT_RUNTIME_DIR}" SSH_KEY="$RUNTIME_DIR/id_ed25519" if [[ ! -d "$RUNTIME_DIR" ]]; then log "runtime bundle not found: $RUNTIME_DIR" log "run 'make runtime-bundle' or set BANGER_RUNTIME_DIR" exit 1 fi if [[ ! -f "$SSH_KEY" ]]; then log "ssh key not found: $SSH_KEY" exit 1 fi wait_for_ssh() { local guest_ip="$1" local deadline=$((SECONDS + 60)) while ((SECONDS < deadline)); do if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ -o ConnectTimeout=2 "root@${guest_ip}" "true" >/dev/null 2>&1; then return 0 fi sleep 1 done return 1 } usage() { cat <<'EOF' Usage: ./verify.sh [--nat] Run a basic smoke test for the Go VM workflow. Use --nat to additionally verify outbound NAT and host rule cleanup. EOF } NAT_ENABLED=0 if [[ "${1:-}" == "--nat" ]]; then NAT_ENABLED=1 shift fi if (($# != 0)); then usage exit 1 fi VM_NAME="verify-$(date +%s)" VM_JSON="" TAP="" VM_DIR="" GUEST_IP="" UPLINK="" cleanup() { if [[ -n "${VM_NAME:-}" ]]; then ./banger vm delete "$VM_NAME" >/dev/null 2>&1 || true fi } trap cleanup EXIT log "starting VM" CREATE_ARGS=(./banger vm create --name "$VM_NAME") if (( NAT_ENABLED )); then CREATE_ARGS+=(--nat) fi "${CREATE_ARGS[@]}" >/dev/null VM_JSON="$(./banger vm show "$VM_NAME")" name="$(printf '%s\n' "$VM_JSON" | jq -r '.name // empty')" guest_ip="$(printf '%s\n' "$VM_JSON" | jq -r '.runtime.guest_ip // empty')" tap="$(printf '%s\n' "$VM_JSON" | jq -r '.runtime.tap_device // empty')" vm_dir="$(printf '%s\n' "$VM_JSON" | jq -r '.runtime.vm_dir // empty')" if [[ -z "$name" || -z "$guest_ip" || -z "$tap" || -z "$vm_dir" ]]; then log "missing VM metadata from banger vm show" exit 1 fi TAP="$tap" VM_DIR="$vm_dir" GUEST_IP="$guest_ip" if (( NAT_ENABLED )); then UPLINK="$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')" if [[ -z "$UPLINK" ]]; then log "failed to detect uplink interface" exit 1 fi log "asserting NAT rules are installed" sudo iptables -t nat -C POSTROUTING -s "${GUEST_IP}/32" -o "$UPLINK" -j MASQUERADE sudo iptables -C FORWARD -i "$TAP" -o "$UPLINK" -j ACCEPT sudo iptables -C FORWARD -i "$UPLINK" -o "$TAP" -m state --state RELATED,ESTABLISHED -j ACCEPT fi log "asserting VM is reachable via SSH" if ! wait_for_ssh "$guest_ip"; then log "ssh did not become ready for ${guest_ip}" exit 1 fi ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ "root@${guest_ip}" "uname -a" >/dev/null if (( NAT_ENABLED )); then log "asserting VM has outbound network access" ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ "root@${guest_ip}" "curl -fsS https://example.com >/dev/null" >/dev/null fi log "cleaning up VM" cleanup log "asserting cleanup success" if ./banger vm show "$VM_NAME" >/dev/null 2>&1; then log "vm still exists after delete: $VM_NAME" exit 1 fi if ip link show "$TAP" >/dev/null 2>&1; then log "tap still exists: $TAP" exit 1 fi if [[ -d "$VM_DIR" ]]; then log "vm dir still exists: $VM_DIR" exit 1 fi if (( NAT_ENABLED )); then if sudo iptables -t nat -C POSTROUTING -s "${GUEST_IP}/32" -o "$UPLINK" -j MASQUERADE 2>/dev/null; then log "nat rule still exists for ${GUEST_IP}" exit 1 fi if sudo iptables -C FORWARD -i "$TAP" -o "$UPLINK" -j ACCEPT 2>/dev/null; then log "forward-out rule still exists for ${TAP}" exit 1 fi if sudo iptables -C FORWARD -i "$UPLINK" -o "$TAP" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null; then log "forward-in rule still exists for ${TAP}" exit 1 fi fi log "ok"