Skip to content

Terminal Access ​

Students get a real shell in their lab containers from the browser. There is no SSH server involved: the bridge uses the LXD control plane.

Architecture ​

Connection sequence ​

Details ​

TopicBehaviorSource
ServerPython websockets 12 (the bridge refuses to start with another major version)cyberrange/bridge-src/bridge.py, requirements.txt
BindBRIDGE_BIND_HOST:BRIDGE_BIND_PORT; the README and manual use 10.115.77.1:8765. The code's fallback host is 10.0.10.11, so the env file must set itbridge.py, bridge-src/README.md
Container per tabpod_type kali, meta or dvwa maps to the lab row's vmid_* container namecontainer_name_for()
User per containerkali and dvwa: student; meta: msfadminPOD_USERS
SessionA reattachable tmux session named lab (history limit 10000), falling back to a login shell if tmux is missing. Reconnecting reattaches the same session, so running programs survive a page reload.launch_command_for()
One viewer per containerA new connection to the same container closes the previous one (its process group is terminated and its WebSocket closed)_evict_existing_session()
First sizeThe bridge waits up to 1 second for the browser's first resize message so the session opens at the real size (falling back to 24x80); keystrokes that arrive first are replayed_first_client_size()
ResizeThe browser sends {"type":"resize","cols":N,"rows":M}; sizes are clamped to 5 to 200 rows and 20 to 500 columns. The bridge resizes its own PTY and signals lxc exec, but lxc exec does not carry a mid-session resize into the container, so since pull request 212 it also runs a short root command in the container that sets the TTY size of every client attached to the lab tmux session and sends that client SIGWINCH. Bursts of resizes (dragging a window) are collapsed into one run with the latest sizeparse_client_message(), tmux_resize_command_for(), ResizeCoalescer
ReconnectSince pull request 224 the browser reconnects a dropped terminal automatically, retrying after 1, 2 and 4 seconds and then showing a Reconnect button. It does not retry on close codes 1008 (authorization) or 1011 (internal error, for example a missing container). The backoff resets only after the bridge confirms [term] Authenticated and the session stays up for 5 seconds, so a second tab that keeps evicting the first cannot loop forever. Because the shell lives in tmux, a reconnect reattaches the same sessionportal/src/components/terminal/XtermView.tsx, terminalSessionManager.ts
Idlenginx keeps the WebSocket open for up to 3600 seconds without trafficcyberrange/deploy/guacamole/nginx-ampere.conf
ErrorsBad input or authorization: message in the terminal and close code 1008; internal errors: close 1011handler()
ConfigAUTH_ENABLED, KEYCLOAK_INTROSPECT_URL, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, PROVISION_API_URL, BRIDGE_BIND_HOST, BRIDGE_BIND_PORT in bridge-src/.env.bridge (mode 600).env.bridge.example
Serviceuser unit cyberrange-ssh-bridge, virtualenv Python, logs in /tmp/ssh-bridge.logcyberrange/deploy/systemd/cyberrange-ssh-bridge.service

Why tmux matters for scoring ​

Scenario 1 Milestone 4 checks the student's tmux lab session directly: the pane must be running Metasploit, show a successful tomcat_mgr_deploy session to meta, and hold a live connection to it (check_live_tomcat_msf_session in cyberrange/src/provisioning/scoring_checks.sh). Keeping the shell in a named tmux session is what makes that check possible.

Security notes ​

  • Token in the URL. The access token travels as a query parameter. Query strings are commonly written to proxy access logs; the gateway nginx has no custom access_log setting, so its default access log would record them. See Findings.
  • Admins can open any lab. The ownership check calls /pods/{id}/status, which allows the owner or an admin. No admin page uses this, but an admin token works for any lab. Instructors cannot open student terminals.
  • Root inside the lab only. Students can sudo inside their own containers, which are unprivileged LXD containers.
  • Bypass mode. AUTH_ENABLED=false skips introspection and ownership (development only; logged as a warning).

Sources

cyberrange/bridge-src/bridge.py, test_bridge.py, README.md, .env.bridge.example, cyberrange/portal/src/components/terminal/XtermView.tsx, terminalSessionManager.ts, cyberrange/deploy/guacamole/nginx-ampere.conf, cyberrange/src/provisioning/auth.py.

Cyber Range capstone documentation (Capstone 2 · Group 2 · A4102). Documents main at commit 6971373.