[Unit] Description=CIS490 lab-host episode orchestrator (fleet mode) Documentation=https://maxgit.wg/spectral/CIS490 # Episodes need KVM. msfrpcd (for Tier 3+) is brought up out-of-band # by cis490-msfrpcd.service when installed. After=network-online.target wg-quick@wg0.service Wants=network-online.target [Service] Type=simple User=cis490 Group=cis490 WorkingDirectory=/opt/cis490 # /etc/cis490/lab-host.env is written by scripts/install-lab-host.sh; # carries FLEET_HOST_ID, BRIDGE, and any operator-supplied overrides. EnvironmentFile=/etc/cis490/lab-host.env # msfrpc credentials (written by install-msfrpcd.sh). Optional (-) so the # unit still starts on Tier-2-only hosts where msfrpcd isn't installed. EnvironmentFile=-/etc/cis490/msfrpc.env # Fleet mode: detect host capacity, run that many concurrent episodes # per wave with samples + experiment shape drawn from the canonical # manifest at /opt/cis490/manifest.toml. Each invocation runs one wave # and exits; systemd respawns per Restart= below. # # Per PIPELINE.md ยง4.1 there are no --manifest, --max-tier3-slots, # --ram-per-vm-mib, --max-concurrent, --force-tier2, or # --require-real-samples flags. Experiment-shape parameters live in # manifest.toml. Per-host overrides are forbidden. # # Exit 78 (sysadmin error) when the canonical manifest fails to load # or when the host can't run the experiment. RestartPreventExitStatus=78 # keeps the unit stuck-and-loud rather than respawning into the same # broken state โ€” operator notices and fixes. ExecStart=/opt/cis490/.venv/bin/python /opt/cis490/tools/run_fleet.py \ --data-root /var/lib/cis490/data \ --waves 1 Restart=always RestartSec=15 RestartPreventExitStatus=78 # Hardening โ€” explicitly grant CAP_NET_RAW for tcpdump (source 4) and # CAP_SYS_ADMIN / CAP_PERFMON for perf (source 3) when the operator # enables those. Both are inherited by per-episode subprocesses. # NoNewPrivileges=false is required because AmbientCapabilities only # survives across exec() if NNP is off. NoNewPrivileges=false PrivateTmp=true ProtectSystem=strict ProtectHome=true # /tmp is needed for per-slot RUN_DIR (cis490-vm-fleet-) โ€” the # fleet runner stages QEMU's sockets + pidfile there. ReadWritePaths=/var/lib/cis490 /tmp SupplementaryGroups=kvm AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_PERFMON CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_PERFMON CAP_DAC_READ_SEARCH [Install] WantedBy=multi-user.target