0265afa054
Import the runnable game code, content, docs, scripts, and repo guidance while leaving local agent state, dependency installs, build output, and backup copies out of the published tree.
207 lines
7.2 KiB
Bash
207 lines
7.2 KiB
Bash
#!/usr/bin/env bash
|
|
# first-run-setup.sh — Sysadmin Chronicles one-time host setup
|
|
#
|
|
# This script creates the libvirt resources (networks, storage pool) and SSH
|
|
# keys required for the game. It is safe to re-run — all actions are idempotent.
|
|
#
|
|
# What this does:
|
|
# 1. Creates sc-internal libvirt network (private NAT, game-only)
|
|
# 2. Creates sc-images storage pool for qcow2 VM images
|
|
# 3. Generates the host→guest SSH key pair (sc_host_key)
|
|
# 4. Checks libvirtd is running and user has access
|
|
#
|
|
# What this does NOT do:
|
|
# - Build VM images (that's seed-vms.sh)
|
|
# - Modify /etc/libvirt or system-wide config beyond the named libvirt resources
|
|
# - Require broad sudo during normal gameplay
|
|
#
|
|
# AGENT RULES: Never run provisioning scripts against VMs from here.
|
|
# This script only creates host infrastructure.
|
|
|
|
set -euo pipefail
|
|
|
|
OWNER_USER="${SUDO_USER:-$USER}"
|
|
OWNER_HOME="$(getent passwd "$OWNER_USER" | cut -d: -f6)"
|
|
OWNER_HOME="${OWNER_HOME:-$HOME}"
|
|
export LIBVIRT_DEFAULT_URI="${LIBVIRT_DEFAULT_URI:-qemu:///system}"
|
|
|
|
DRY_RUN=false
|
|
if [[ "${1:-}" == "--dry-run" ]]; then
|
|
DRY_RUN=true
|
|
echo "[DRY-RUN] No changes will be made."
|
|
fi
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
run() {
|
|
if [ "$DRY_RUN" = "true" ]; then
|
|
echo " [would run] $*"
|
|
else
|
|
"$@"
|
|
fi
|
|
}
|
|
|
|
step() { echo ""; echo "── $* ───────────────────────"; }
|
|
ok() { echo " ✓ $*"; }
|
|
info() { echo " → $*"; }
|
|
|
|
libvirt_reachable() {
|
|
virsh -q list --all >/dev/null 2>&1
|
|
}
|
|
|
|
libvirt_socket_available() {
|
|
systemctl is-active --quiet libvirtd.socket 2>/dev/null || \
|
|
systemctl is-active --quiet virtqemud.socket 2>/dev/null
|
|
}
|
|
|
|
echo ""
|
|
echo "══════════════════════════════════════════════════"
|
|
echo " Sysadmin Chronicles — First-Run Setup"
|
|
echo "══════════════════════════════════════════════════"
|
|
|
|
# Sanity: check libvirt access
|
|
step "Checking libvirt access"
|
|
if libvirt_reachable; then
|
|
ok "libvirt responds to virsh"
|
|
elif libvirt_socket_available; then
|
|
ok "libvirt socket activation is available"
|
|
else
|
|
echo " ERROR: libvirt is not reachable."
|
|
echo " Run: sudo systemctl enable --now libvirtd.socket"
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
step "Creating sc-internal libvirt network"
|
|
# ---------------------------------------------------------------------------
|
|
|
|
NETWORK_XML="$SCRIPT_DIR/../vm/network-sc-internal.xml"
|
|
|
|
write_network_xml() {
|
|
local target="$1"
|
|
cat > "$target" << 'EOF'
|
|
<network>
|
|
<name>sc-internal</name>
|
|
<forward mode='nat'/>
|
|
<bridge name='sc-br0' stp='on' delay='0'/>
|
|
<ip address='10.42.0.1' netmask='255.255.255.0'>
|
|
<dhcp>
|
|
<range start='10.42.0.10' end='10.42.0.50'/>
|
|
</dhcp>
|
|
</ip>
|
|
</network>
|
|
EOF
|
|
}
|
|
|
|
network_has_nat() {
|
|
virsh net-dumpxml sc-internal 2>/dev/null | grep -q "<forward mode=['\"]nat['\"]"
|
|
}
|
|
|
|
define_network() {
|
|
local label="$1"
|
|
local tmpxml
|
|
tmpxml=$(mktemp /tmp/sc-internal-XXXXXX.xml)
|
|
if [ -f "$NETWORK_XML" ]; then
|
|
cp "$NETWORK_XML" "$tmpxml"
|
|
else
|
|
write_network_xml "$tmpxml"
|
|
fi
|
|
run virsh net-define "$tmpxml"
|
|
run virsh net-autostart sc-internal
|
|
run virsh net-start sc-internal
|
|
rm -f "$tmpxml"
|
|
ok "$label"
|
|
}
|
|
|
|
if virsh net-list --all | grep -q "sc-internal"; then
|
|
if network_has_nat; then
|
|
ok "sc-internal network already exists with NAT"
|
|
else
|
|
info "Recreating sc-internal with NAT egress for guest provisioning..."
|
|
run virsh net-destroy sc-internal >/dev/null 2>&1 || true
|
|
run virsh net-undefine sc-internal
|
|
define_network "sc-internal network recreated and started"
|
|
fi
|
|
else
|
|
info "Creating sc-internal (private NAT, game-scoped network)..."
|
|
define_network "sc-internal network created and started"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
step "Creating sc-images storage pool"
|
|
# ---------------------------------------------------------------------------
|
|
|
|
if [ "${LIBVIRT_DEFAULT_URI}" = "qemu:///system" ]; then
|
|
IMAGES_DIR="/var/lib/libvirt/images/sysadmin-chronicles"
|
|
else
|
|
IMAGES_DIR="$OWNER_HOME/.local/share/sysadmin-chronicles/images"
|
|
fi
|
|
|
|
if virsh pool-list --all | grep -q "sc-images"; then
|
|
CURRENT_POOL_PATH="$(virsh pool-dumpxml sc-images 2>/dev/null | sed -n 's:.*<path>\(.*\)</path>.*:\1:p' | head -n1)"
|
|
if [ "$CURRENT_POOL_PATH" = "$IMAGES_DIR" ]; then
|
|
ok "sc-images pool already exists"
|
|
else
|
|
info "Recreating sc-images pool at $IMAGES_DIR (was $CURRENT_POOL_PATH)..."
|
|
run mkdir -p "$IMAGES_DIR"
|
|
run virsh pool-destroy sc-images >/dev/null 2>&1 || true
|
|
run virsh pool-undefine sc-images
|
|
run virsh pool-define-as sc-images dir --target "$IMAGES_DIR"
|
|
run virsh pool-autostart sc-images
|
|
run virsh pool-start sc-images
|
|
ok "sc-images pool recreated"
|
|
fi
|
|
else
|
|
info "Creating sc-images pool at $IMAGES_DIR..."
|
|
run mkdir -p "$IMAGES_DIR"
|
|
run virsh pool-define-as sc-images dir --target "$IMAGES_DIR"
|
|
run virsh pool-autostart sc-images
|
|
run virsh pool-start sc-images
|
|
ok "sc-images pool created"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
step "Generating SSH key pair"
|
|
# ---------------------------------------------------------------------------
|
|
|
|
KEY_PATH="$OWNER_HOME/.ssh/sc_host_key"
|
|
if [ -f "$KEY_PATH" ]; then
|
|
run chown "$OWNER_USER:$OWNER_USER" "$KEY_PATH" "${KEY_PATH}.pub" >/dev/null 2>&1 || true
|
|
run chmod 600 "$KEY_PATH" >/dev/null 2>&1 || true
|
|
run chmod 644 "${KEY_PATH}.pub" >/dev/null 2>&1 || true
|
|
ok "SSH key already exists: $KEY_PATH"
|
|
else
|
|
info "Generating ed25519 key: $KEY_PATH"
|
|
run mkdir -p "$(dirname "$KEY_PATH")"
|
|
run ssh-keygen -t ed25519 -N "" -C "sysadmin-chronicles-host" -f "$KEY_PATH"
|
|
run chmod 600 "$KEY_PATH"
|
|
run chmod 644 "${KEY_PATH}.pub"
|
|
run chown "$OWNER_USER:$OWNER_USER" "$KEY_PATH" "${KEY_PATH}.pub"
|
|
ok "SSH key generated"
|
|
info "Public key (add to VM authorized_keys during build):"
|
|
if [ "$DRY_RUN" = "false" ]; then
|
|
cat "${KEY_PATH}.pub"
|
|
fi
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
step "Verifying group membership"
|
|
# ---------------------------------------------------------------------------
|
|
|
|
if id -nG | grep -qw "libvirt"; then
|
|
ok "User is in libvirt group"
|
|
else
|
|
info "Consider adding yourself to the libvirt group for passwordless VM control:"
|
|
info " sudo usermod -aG libvirt \$USER && newgrp libvirt"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
echo ""
|
|
echo "══════════════════════════════════════════════════"
|
|
echo " Setup complete."
|
|
echo " Next step: bash tools/setup/seed-vms.sh"
|
|
echo "══════════════════════════════════════════════════"
|
|
echo ""
|