You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed as a single feature / tracking issue (owner decision — not an epic with
child issues). The phased breakdown below is tracked as sub-tasks within this issue.
Depends on #266 (on-demand per-team challenge instances): a workstation is
effectively another instance kind on the same provisioner/routing/lifecycle/
hardening substrate. The A/D-integration sub-task additionally depends on #267.
Positioning: removes the "install your own tooling" barrier — the HTB Pwnbox /
THM AttackBox pattern, which no self-hosted jeopardy platform bundles.
Summary
Competitors currently need their own configured attack VM (Kali/Parrot) to play. This
adds a per-user (or per-team) browser-accessible workstation: a web terminal and/or
full GUI desktop running a curated tooling image, launched from the challenge UI and
reachable over the existing reverse proxy, authenticated to the platform session.
A workstation is modelled as a new instance kind on the #266 provisioner, with its
own lifecycle (one active per user, longer TTL, extend/reset) and a stricter network
policy.
Motivation & competitive context
Requiring local tooling is a real barrier for education, beginners, on-site events, and
locked-down corporate/exam environments. HackTheBox Pwnbox and TryHackMe AttackBox
prove the demand and the retention value. No self-hosted CTF platform (CTFd, rCTF) ships
an in-browser workstation. Delivered on top of the instancing feature, this is a
distinctive, education-and-enterprise-friendly differentiator.
Browser access: a web terminal (PTY over WebSocket) and, later, a GUI
desktop (noVNC), proxied through Caddy and bound to the user's authenticated session
via short-lived signed tokens (mirroring signed-attachment URLs).
Lifecycle: one active workstation per user; TTL + idle reaping; extend and reset (wipe & recreate); auto-reap at competition end.
Network policy: the workstation may reach permitted challenge instances (and, in
A/D, opponents' services) but not the platform control plane, other users'
workstations, or the internet — with a per-competition egress toggle.
Delivered as an opt-in module, off by default, per-competition toggle.
Non-goals (initial)
Persistent per-user home directories across events (optional later; needs storage).
Arbitrary user-supplied images (curated organiser images only in v1).
Windows workstations.
Proposed design
Workstation as an instance kind
Reuse the Provisioner abstraction and lifecycle from #266; add a workstation kind
with its own spec (image, resources, GUI vs terminal, egress policy) and lifecycle policy
(one-active-per-user, longer TTL, reset). Propose a short ADR only for the access/auth
model and network policy, if not already covered by the instancing ADR.
Access layer
Terminal: a PTY-over-WebSocket broker (ttyd/wetty-style) proxied through Caddy; the
WS is authorised by a short-lived signed token scoped to the instance and user.
GUI (later): noVNC → a VNC server in the container (lightweight XFCE), or Apache
Guacamole (guacd) if RDP/VNC/SSH multiplexing is wanted. Document the trade-off
(noVNC simpler; Guacamole heavier but more capable).
Session binding & presence
Only the owning user may attach; token scoped to the instance. Integrate with the
presence system ("in workstation"). Push status over WebSockets like other instances.
Networking / isolation
Default deny egress; per-competition opt-in (some challenges need internet).
Allowlist to the challenge-instance network only; deny platform-internal
services (Postgres, Redis, MinIO, API control plane) and peer workstations.
On cloud, block the metadata IP 169.254.169.254; apply seccomp/AppArmor, dropped
capabilities, no-new-privileges, non-root where feasible.
One active workstation per user; TTL + idle detection (reap idle sessions); extend and reset; auto-reap at competition end. Enforce a global concurrency ceiling
(workstations are heavier than challenge instances).
Frontend
A "Launch workstation" control (global for the competition, not per challenge).
Embedded terminal/desktop panel (component or iframe) with status, countdown, extend, reset.
Clear messaging when at capacity or when egress is disabled.
Security (critical — these are shells with network reach)
Beyond the shared #266 hardening: strict egress control (prevent using workstations to
attack third parties or mine crypto), launch auditing and rate limits, cloud-metadata
blocking, and a clear acceptable-use note surfaced at launch. Treat capacity/cost as a
first-class concern.
Scaling
Workstations are the heaviest instance kind. Provide sizing guidance and concurrency
caps; the Kubernetes backend (and the existing Fargate/ALB deployment path, ADR-0031) is
the natural home for large events.
A competitor can launch a browser terminal (later, desktop) from the competition UI,
authenticated to their session, and use it against permitted challenge instances.
One active workstation per user; idle/TTL reaping and a global concurrency cap work.
The workstation cannot reach the platform control plane, peer workstations, or the
internet unless egress is explicitly enabled.
The feature is off by default and toggled per competition.
Risks & open questions
Cost & capacity — the biggest risk; workstations are heavy. Concurrency caps and
sizing guidance are mandatory.
Security of handing users shells with network reach — isolation and egress control
are non-negotiable; cloud-metadata exposure must be blocked.
Egress abuse (attacks on third parties, mining) — default-deny + AUP + audit.
Per-user vs per-team workstations — decide early.
Persistence — whether/when to offer durable home directories.
Derived from an internal feature brief. This is the canonical tracking issue for the feature.
Summary
Competitors currently need their own configured attack VM (Kali/Parrot) to play. This
adds a per-user (or per-team) browser-accessible workstation: a web terminal and/or
full GUI desktop running a curated tooling image, launched from the challenge UI and
reachable over the existing reverse proxy, authenticated to the platform session.
A workstation is modelled as a new instance kind on the #266 provisioner, with its
own lifecycle (one active per user, longer TTL, extend/reset) and a stricter network
policy.
Motivation & competitive context
Requiring local tooling is a real barrier for education, beginners, on-site events, and
locked-down corporate/exam environments. HackTheBox Pwnbox and TryHackMe AttackBox
prove the demand and the retention value. No self-hosted CTF platform (CTFd, rCTF) ships
an in-browser workstation. Delivered on top of the instancing feature, this is a
distinctive, education-and-enterprise-friendly differentiator.
Goals
scale), running a curated tooling image.
desktop (noVNC), proxied through Caddy and bound to the user's authenticated session
via short-lived signed tokens (mirroring signed-attachment URLs).
reset (wipe & recreate); auto-reap at competition end.
A/D, opponents' services) but not the platform control plane, other users'
workstations, or the internet — with a per-competition egress toggle.
handling.
Non-goals (initial)
Proposed design
Workstation as an instance kind
Reuse the
Provisionerabstraction and lifecycle from #266; add aworkstationkindwith its own spec (image, resources, GUI vs terminal, egress policy) and lifecycle policy
(one-active-per-user, longer TTL, reset). Propose a short ADR only for the access/auth
model and network policy, if not already covered by the instancing ADR.
Access layer
WS is authorised by a short-lived signed token scoped to the instance and user.
Guacamole (
guacd) if RDP/VNC/SSH multiplexing is wanted. Document the trade-off(noVNC simpler; Guacamole heavier but more capable).
Session binding & presence
Only the owning user may attach; token scoped to the instance. Integrate with the
presence system ("in workstation"). Push status over WebSockets like other instances.
Networking / isolation
services (Postgres, Redis, MinIO, API control plane) and peer workstations.
169.254.169.254; apply seccomp/AppArmor, droppedcapabilities,
no-new-privileges, non-root where feasible.(First-class Attack-Defense & King-of-the-Hill competition modes #267).
Lifecycle
One active workstation per user; TTL + idle detection (reap idle sessions); extend and
reset; auto-reap at competition end. Enforce a global concurrency ceiling
(workstations are heavier than challenge instances).
Frontend
extend, reset.
RBAC & config
workstation_launch..env: workstation image, resource limits, GUI-vs-terminal, egress policy,concurrency cap, idle timeout.
Security (critical — these are shells with network reach)
Beyond the shared #266 hardening: strict egress control (prevent using workstations to
attack third parties or mine crypto), launch auditing and rate limits, cloud-metadata
blocking, and a clear acceptable-use note surfaced at launch. Treat capacity/cost as a
first-class concern.
Scaling
Workstations are the heaviest instance kind. Provide sizing guidance and concurrency
caps; the Kubernetes backend (and the existing Fargate/ALB deployment path, ADR-0031) is
the natural home for large events.
Phasing
reaping; session-bound WS proxy; egress off; curated image; launch UI.
lifecycle events + automation.
egress; A/D integration (reach opponents); metadata/cloud-cred hardening.
Proposed breakdown (sub-tasks tracked within this issue)
workstationinstance kind + spec + one-active-per-user lifecycle (longer TTL; reset semantics) — depends on On-demand, per-team containerised challenge instances #266workstation_launch) + per-competition module toggle + global configworkstation.started/.expired/.reset) + automation actionsOverall acceptance criteria
authenticated to their session, and use it against permitted challenge instances.
internet unless egress is explicitly enabled.
Risks & open questions
sizing guidance are mandatory.
are non-negotiable; cloud-metadata exposure must be blocked.
Derived from an internal feature brief. This is the canonical tracking issue for the feature.