fix(ssh): per-challenge SSH login + phew buffering/leak/timeout
Passwords failed on 10/16 challenges while state.json looked correct:
- only the 6 native GEMASTIK XVIII images provision 'ctfuser'; every imported
XVI/XVII image does 'echo root:${PASSWORD} | chpasswd' and logs in as root.
set_ssh_passwords() hardcoded ctfuser, so chpasswd set a password on an
account nobody uses -> 'Permission denied' everywhere.
Registry gains a per-challenge 'ssh_user'; chpasswd now targets the real
login (and ctfuser/ctf when present) and reports failures loudly.
- phew checker: chall.py block-buffers stdout through the docker exec pipe
(PYTHONUNBUFFERED now set) and leaks chall.py inside the container on
timeout (26 orphans, container saturated) -> reaps the whole exec process
group. Startup does a fresh Pailier keygen (~12 s) so crypto reads need
_CRYPTO_TIMEOUT, not the 5 s prompt default.
Adds panel/verify_ssh_creds.py (proves the state->container binding from
inside via a real login), audit_ssh_users.sh, reset_runtime.sh.
This commit is contained in:
@@ -14,6 +14,14 @@ from pathlib import Path
|
||||
TEAMS_DIR = Path("/opt/gemastik18-final/teams")
|
||||
RECEIVER_VENV = "/opt/gemastik18-final/receiver/.venv/bin/python"
|
||||
UNIT_DIR = Path("/etc/systemd/system")
|
||||
REGISTRY_PATH = TEAMS_DIR / "challenge_registry.json"
|
||||
|
||||
|
||||
def load_registry() -> dict:
|
||||
try:
|
||||
return json.loads(REGISTRY_PATH.read_text())
|
||||
except Exception:
|
||||
return {"sets": {}, "challenges": []}
|
||||
|
||||
def write_unit(idx: int, st: dict):
|
||||
port = st["ports"]["receiver"]
|
||||
@@ -23,12 +31,16 @@ def write_unit(idx: int, st: dict):
|
||||
# NOTE: systemd Environment= keys must be [A-Za-z0-9_]+ — a hyphen in the
|
||||
# challenge name (gift-card) would make systemd silently drop the line, so
|
||||
# normalize the name to underscores here. main.py looks up the same key.
|
||||
# Same normalization applies to CHALLENGE_SCHEME_<NAME>.
|
||||
schemes = {c["name"]: c.get("scheme") for c in load_registry().get("challenges", [])}
|
||||
for ch in st["ports"]:
|
||||
if ch in ("receiver", "panel"):
|
||||
continue
|
||||
key = ch.upper().replace("-", "_")
|
||||
env[f"CHALLENGE_PORT_{key}"] = str(st["ports"][ch]["chall"])
|
||||
env[f"CHALLENGE_CONTAINER_{key}"] = f"{ch}_container_team{idx}"
|
||||
if schemes.get(ch):
|
||||
env[f"CHALLENGE_SCHEME_{key}"] = schemes[ch]
|
||||
# SSH passwords: checker Challenge.credentials() reads PASSWORD_<self.port>
|
||||
# where self.port is the team challenge port.
|
||||
pwd = st.get("chall_passwords", {}).get(ch)
|
||||
|
||||
Reference in New Issue
Block a user