#!/usr/bin/env python3 """Verify /api/credential reports the SSH user the container ACTUALLY has. The panel proxies to the global receiver, which only knows the 6 native GEMASTIK XVIII challenges -- the 10 imported XVI/XVII ones 500 there. For those the UI must read the credential from the TEAM's own receiver (which is the one that actually runs the checkers), not from :18080. This test reports, per team, the username /credential would show vs the `ssh_user` the registry (and chpasswd) uses. """ import json import os import subprocess import sys import urllib.request import urllib.error import base64 from pathlib import Path BASE = Path("/opt/gemastik18-final") ENV = BASE / "panel/.env" cfg = {} for line in ENV.read_text().splitlines(): if "=" in line and not line.startswith("#"): k, v = line.split("=", 1) cfg[k.strip()] = v.strip() PANEL = "http://127.0.0.1:18081" def post(path, payload, cookie=None): data = json.dumps(payload).encode() req = urllib.request.Request(PANEL + path, data=data, method="POST", headers={"Content-Type": "application/json"}) if cookie: req.add_header("Cookie", cookie) with urllib.request.urlopen(req, timeout=30) as r: return r.read().decode(), r.headers.get("Set-Cookie", "") body, setc = post("/api/login", {"user": cfg.get("PANEL_ADMIN_USER", "admin"), "pass": cfg.get("PANEL_ADMIN_PASS", "")}) cookie = "; ".join(s.split(";")[0] for s in setc.split(",") if "=" in s) print("login:", body) def get(path): req = urllib.request.Request(PANEL + path, headers={"Cookie": cookie}) try: with urllib.request.urlopen(req, timeout=60) as r: return r.read().decode() except urllib.error.HTTPError as e: return f"HTTP {e.code}" reg = json.loads((BASE / "teams/challenge_registry.json").read_text()) ssh_users = {c["name"]: c.get("ssh_user", "ctfuser") for c in reg.get("challenges", [])} teams = json.loads(get("/api/teams"))["teams"] ok = bad = 0 for t in teams: idx = t["index"] st = json.loads((BASE / f"teams/team{idx}/state.json").read_text()) names = list(st["ports"].keys()) names = [n for n in names if n not in ("receiver", "panel")] print(f"\n=== team{idx} ({t.get('label')}) — {len(names)} challenges ===") for n in sorted(names): raw = get(f"/api/credential/{n}?team={idx}") try: d = json.loads(raw) except Exception: d = {"error": raw[:40]} want = ssh_users.get(n, "?") got = d.get("username") haspw = bool(d.get("password")) if got == want and haspw: print(f" {n:<15} {got:<8} pw={'yes' if haspw else 'NO '} OK") ok += 1 else: note = "" if got else f" <- {d.get('error', raw)[:30]}" print(f" {n:<15} {str(got):<8} want={want:<8} pw={'yes' if haspw else 'NO '}{note}") bad += 1 print(f"\n{ok} correct, {bad} wrong/missing") sys.exit(0)