CIS490/etc/cis490-orchestrator.service
elliott d294eb9f52 fix: orchestrator never received MSFRPC_PASSWORD — load msfrpc.env
The cis490-orchestrator unit only loaded lab-host.env, which has no
MSFRPC_PASSWORD. run_tier3_demo.py exits rc=2 immediately if the var
is unset. All tier3 slots were failing in ~240ms.

Add EnvironmentFile=-/etc/cis490/msfrpc.env (the '-' prefix silences
the error on Tier-2-only hosts where the file doesn't exist yet).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 14:43:40 -06:00

49 lines
2 KiB
Desktop File

[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.env only exists after install-tier-3-4.sh; the '-' prefix makes
# this a no-op on Tier-2-only hosts where it hasn't run yet.
EnvironmentFile=-/etc/cis490/msfrpc.env
# Fleet mode: detect host capacity, run that many concurrent episodes
# per wave with samples drawn from the manifest. Each invocation runs
# one wave and exits; systemd respawns per Restart= below, giving us
# a continuous stream of fresh-sample episodes per host. The shipper
# picks them up as `done.marker` files appear.
ExecStart=/opt/cis490/.venv/bin/python /opt/cis490/tools/run_fleet.py \
--data-root /var/lib/cis490/data \
--manifest /opt/cis490/samples/manifest.toml \
--waves 1
Restart=always
RestartSec=15
# 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-<slot>) — 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