No description
  • Python 89.4%
  • Shell 9.1%
  • Dockerfile 1.5%
Find a file
2026-10-15 01:26:56 -04:00
gateway Extract Lab in a Box as a standalone tool 2026-10-07 17:54:30 -04:00
.gitignore Persist audit logs in separate session files 2026-10-15 01:26:56 -04:00
compose.yaml Persist audit logs in separate session files 2026-10-15 01:26:56 -04:00
containers.json Add session audit logs and allowlisted SSH environment forwarding 2026-10-14 21:53:12 -04:00
deploy.sh Name the SSH service coordinator and use published role tags 2026-10-07 19:07:53 -04:00
Dockerfile Extract Lab in a Box as a standalone tool 2026-10-07 17:54:30 -04:00
network.py Name the SSH service coordinator and use published role tags 2026-10-07 19:07:53 -04:00
README.md Persist audit logs in separate session files 2026-10-15 01:26:56 -04:00
requirements.txt Extract Lab in a Box as a standalone tool 2026-10-07 17:54:30 -04:00
server.py Persist audit logs in separate session files 2026-10-15 01:26:56 -04:00
ssh_config Extract Lab in a Box as a standalone tool 2026-10-07 17:54:30 -04:00

Lab in a box

From a clone of this repo:

./deploy.sh

Open http://localhost:3000/. Exit, disconnect, refresh or expiry deletes the lab and its files. Redeployment ends existing sessions.

Needs ip and either Podman with podman-compose and user systemd, or Docker with Compose and socket access. Podman is preferred when installed; override with CONTAINER_ENGINE=docker. Use CONTAINER_SOCKET for a nonstandard socket.

For a reverse proxy:

ALLOWEDORIGINS=https://terminal.example.com ./deploy.sh

Only localhost HTTP is published. The coordinator’s SSH port stays internal. No authentication or TLS is included.

Configuration

Edit containers.json, then redeploy.

  • default: definition opened automatically. Omit it for a numbered menu. /ssh/<name> opens a definition directly.
  • max_sessions, max_starts_per_minute: global caps, currently 32 and 60.
  • Each definition sets image, optional pretty label, memory_mb, cpus, pids_limit and max_lifetime_seconds.
  • internet: defaults to false; true enables IPv4 TCP/UDP through a separate firewall gateway. Private/reserved networks and captured host routes are blocked.
  • Optional disk_mb: writable-layer quota, e.g. 1024. Unsupported quotas fail the session; omitting it leaves disk usage unlimited.
  • log_session and log_environment: optional booleans, both default false. The first records terminal input/output; the second records the SSH environment received by the coordinator, not the browser service's environment or cookies. Neither changes the student container's environment. Each logged session writes session-logs/<container-name>.jsonl on the host, with UTC timestamps and the container name as session. Set LAB_LOG_DIR to change the host folder. Terminal data is base64; bytes is the original chunk size. Input chunks are received SSH data, not individual keystrokes or definitive paste detection. Files persist after sessions and redeployments until you remove them. A start event marks the launch attempt; end follows its cleanup attempt.
  • forward_environment: optional list of SSH environment variables to pass into the lab, e.g. ["LAB_USERNAME", "LAB_SESSION_ID"]; defaults to []. Missing variables are skipped; TERM stays controlled by the terminal settings. Values are passed as engine environment entries, never shell commands. Only list variables the image should receive; students can change their copies.

Custom images must already exist in the engine and provide /bin/bash. Set their USER and WORKDIR. Deployment pulls Debian and builds the gateway and coordinator. Set LAB_CONFIG=/absolute/path/to/containers.json for an external config. Toolbox images need no networking modifications.

Limits

Internet needs rootless Podman 6+ with Netavark, or Docker 28+. Redeploy after VPN, interface or route changes; blocked routes are a deployment-time snapshot. Podman forwarding and isolation tests passed; physical host/LAN positive-control coverage is incomplete. Docker has not been runtime-tested.

Coordinator compromise grants engine-socket access to the host. Bound engine storage separately to cover images, logs and labs without quotas. Session caps are global and can be occupied by one user.

Startup and shutdown remove labeled leftovers. Run one coordinator per engine. Coordinator logs record cleanup requests, failures and completion.