#!/usr/bin/env python3 """Live health check for the attack-defense platform after the PixiJS work. Uses the panel's own API with credentials read from .env, so no shell quoting hazard and no password on a command line. """ import json import subprocess import urllib.request import http.cookiejar from pathlib import Path BASE = "http://127.0.0.1:18081" ENV = Path("/opt/gemastik18-final/panel/.env") def load_env(): cfg = {} for line in ENV.read_text().splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue k, _, v = line.partition("=") cfg[k.strip()] = v.strip().strip('"').strip("'") return cfg def get(url, cookie=None): req = urllib.request.Request(url) if cookie: req.add_header("Cookie", "; ".join(f"{c.name}={c.value}" for c in cookie)) with urllib.request.urlopen(req, timeout=30) as r: return r.read().decode() def post_json(url, payload, cookie=None): data = json.dumps(payload).encode() req = urllib.request.Request(url, data=data, method="POST", headers={"Content-Type": "application/json"}) if cookie: req.add_header("Cookie", "; ".join(f"{c.name}={c.value}" for c in cookie)) with urllib.request.urlopen(req, timeout=30) as r: return r.read().decode() def main(): cfg = load_env() jar = http.cookiejar.CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar)) # main.py's /api/login expects {"user","pass"} — not username/password. body = json.dumps({"user": cfg["PANEL_ADMIN_USER"], "pass": cfg["PANEL_ADMIN_PASS"]}).encode() req = urllib.request.Request(BASE + "/api/login", data=body, method="POST", headers={"Content-Type": "application/json"}) with opener.open(req, timeout=30) as r: login = json.loads(r.read().decode()) print("login:", login) cookie = jar teams_raw = json.loads(get(BASE + "/api/teams", cookie)) teams = teams_raw.get("teams", teams_raw) if isinstance(teams_raw, dict) else teams_raw print("\nteams:") for t in teams: print(f" #{t.get('index')} {t.get('label')} domain={t.get('domain')} " f"receivers={t.get('receiver_port')} challenges={len(t.get('challenges') or [])}") topo = json.loads(get(BASE + "/api/topology", cookie)) print("\ntopology API:", {k: (len(v) if isinstance(v, list) else v) for k, v in topo.items()}) containers = subprocess.run( ["docker", "ps", "--format", "{{.Names}}"], capture_output=True, text=True, timeout=60).stdout.split() team_ct = [c for c in containers if c.endswith(tuple(f"_team{i}" for i in range(1, 10)))] orphans = [c for c in containers if "_container" in c and not any(c.endswith(f"_team{i}") for i in range(1, 10))] print(f"\ncontainers: {len(team_ct)} team-scoped, orphans={orphans}") services = subprocess.run( ["systemctl", "is-active", "gemastik-panel", "gemastik-receiver-service", "gemastik-receiver-team1", "gemastik-receiver-team2"], capture_output=True, text=True, timeout=60).stdout.strip() print("services:\n ", services.replace("\n", "\n ")) load = subprocess.run(["uptime"], capture_output=True, text=True).stdout.strip() print("load:", load) print("expected team containers:", len(teams) * 16, "| actual:", len(team_ct)) if __name__ == "__main__": main()