#!/usr/bin/env python3 """Proxmox-side fail-closed monitor; only the disposable VM223 is stopped.""" import json import pathlib import shutil import subprocess import time DIRECTORY = pathlib.Path('/var/lib/vz/honeypot') def qm(*args): return subprocess.run(['/usr/sbin/qm', *args], capture_output=True, text=True, timeout=25, check=True).stdout def stop(reason): with (DIRECTORY / 'watchdog-events.jsonl').open('a') as output: output.write(json.dumps(dict(time_ns=time.time_ns(), event='execution_vm_stopped', reason=reason)) + '\n') output.flush() qm('stop', '223') # This latch prevents unattended auto-restart. Operator must verify evidence before clearing. (DIRECTORY / 'STOP').touch(mode=0o600) def main(): last_health = 0 while True: try: if 'status: running' not in qm('status', '223'): time.sleep(2) continue reason = None pcap = DIRECTORY / 'execution.pcap' if (DIRECTORY / 'STOP').exists(): reason = 'host stop latch' elif pcap.exists() and pcap.stat().st_size >= 512 * 1024 * 1024: reason = 'pcap quota' elif shutil.disk_usage(DIRECTORY).free < 4 * 1024**3: reason = 'host free space reserve' elif time.monotonic() - last_health >= 10: last_health = time.monotonic() usage = subprocess.run(['/usr/sbin/lvs', '--noheadings', '-o', 'data_percent', 'pve/data'], capture_output=True, text=True, check=True, timeout=5) if float(usage.stdout.strip()) >= 85: stop('Proxmox thin pool reserve') continue try: result = json.loads(qm('guest', 'exec', '222', '--timeout', '10', '--', '/bin/sh', '-c', 'test ! -e /var/lib/decoy/STOP && ' 'systemctl is-active --quiet decoy.service decoy-network.service')) if result.get('exitcode') != 0 or not result.get('exited'): reason = 'collector services stopped or capture failed' except (OSError, ValueError, subprocess.SubprocessError): reason = 'collector health unavailable' if reason: stop(reason) except (OSError, subprocess.SubprocessError) as error: print(type(error).__name__, flush=True) time.sleep(1) if __name__ == '__main__': main()