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.
70 lines
2.9 KiB
Bash
70 lines
2.9 KiB
Bash
#!/usr/bin/env bash
|
|
# FULL reset of the runtime — keeps ONLY the shared challenge images.
|
|
#
|
|
# Removes: every team container, every team docker network, every anonymous/
|
|
# named volume, every per-team receiver systemd unit, and all team directories
|
|
# (state, flags, credentials, compose). KEEPS: services-* images (the
|
|
# challenges themselves), the panel, the global receiver, the challenge sources
|
|
# in services/, and the registry.
|
|
#
|
|
# This is the "purge everything except the challenges" path the organiser asked
|
|
# for after passwords drifted: fresh containers get fresh /etc/shadow state, so
|
|
# no stale credential can survive.
|
|
set -uo pipefail
|
|
BASE=/opt/gemastik18-final
|
|
TEAMS=$BASE/teams
|
|
|
|
echo "=== [1/6] stop per-team receivers + remove units ==="
|
|
for u in $(systemctl list-unit-files 'gemastik-receiver-team*.service' 2>/dev/null \
|
|
| awk '/gemastik-receiver-team/{print $1}'); do
|
|
systemctl disable --now "$u" >/dev/null 2>&1
|
|
rm -f "/etc/systemd/system/$u"
|
|
echo " removed $u"
|
|
done
|
|
systemctl daemon-reload
|
|
|
|
echo "=== [2/6] compose down for every team (before deleting dirs) ==="
|
|
for d in "$TEAMS"/team*/services; do
|
|
[ -d "$d" ] || continue
|
|
idx=$(basename "$(dirname "$d")")
|
|
echo " compose down $idx"
|
|
(cd "$d" && docker compose -p "$idx" -f docker-compose.yml down -v --remove-orphans 2>&1 | tail -1)
|
|
done
|
|
|
|
echo "=== [3/6] force-remove any surviving team containers ==="
|
|
left=$(docker ps -aq --filter 'name=_container_team' | wc -l)
|
|
echo " found $left"
|
|
[ "$left" -gt 0 ] && docker rm -f $(docker ps -aq --filter 'name=_container_team') >/dev/null 2>&1
|
|
|
|
echo "=== [4/6] remove team networks + stray volumes ==="
|
|
for n in $(docker network ls --format '{{.Name}}' | grep -E '^team[0-9]+_default$' || true); do
|
|
docker network rm "$n" >/dev/null 2>&1 && echo " network $n"
|
|
done
|
|
# anonymous + team-scoped volumes (challenge DB state lives here)
|
|
anon=$(docker volume ls -q --filter dangling=true | wc -l)
|
|
echo " dangling volumes: $anon"
|
|
[ "$anon" -gt 0 ] && docker volume prune -f >/dev/null 2>&1 && echo " pruned"
|
|
for v in $(docker volume ls --format '{{.Name}}' | grep -E '^team[0-9]+' || true); do
|
|
docker volume rm "$v" >/dev/null 2>&1 && echo " volume $v"
|
|
done
|
|
|
|
echo "=== [5/6] delete team dirs + ledgers ==="
|
|
rm -rf "$TEAMS"/team*
|
|
rm -f "$TEAMS"/leaderboard.json "$TEAMS"/points.json "$TEAMS"/attacks.json
|
|
echo " team dirs now: $(ls -d "$TEAMS"/team* 2>/dev/null | wc -l)"
|
|
|
|
echo "=== [6/6] drop team domains from Traefik ==="
|
|
cd "$BASE/panel" && python3 -c "
|
|
import sys; sys.path.insert(0,'.')
|
|
import teams
|
|
print(' ', teams.ensure_team_domains())
|
|
"
|
|
# the platform-level receiver is separate and must keep running
|
|
systemctl restart gemastik-panel 2>/dev/null
|
|
echo " panel: $(systemctl is-active gemastik-panel)"
|
|
|
|
echo "=== done ==="
|
|
docker ps --format '{{.Names}}' | grep -c '_container_team' || echo " team containers: 0"
|
|
docker images --format '{{.Repository}}' | grep -c '^services-' || true
|
|
df -h / | tail -1
|