CitrixPot: NetScaler / Citrix ADC Gateway honeypot (initial public release)

This commit is contained in:
CitrixPot committed 2026-09-28 19:44:37 +02:00
commit 4491b42714
54 files changed
+4532

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
#!/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()