Skip to content

Technology Decisions ​

What each major technology does in this system, and why the architecture uses it. Reasons are taken from code comments, the operator manual and design documents in the repository. Where the repository states what is used but not why, the reason is marked as inferred.

Summary table ​

TechnologyRole in this systemWhy it is used hereEvidence
Oracle Cloud Infrastructure (OCI), Ampere A1Hosts the entire platform on one ARM64 virtual machine (2 OCPU, 12 GiB RAM)Fits the OCI Always Free allowance, so the capstone runs at no cloud cost. Everything else is sized for 12 GiB.manual/README.md, manual/01-oci-host-and-network.md, cyberrange/src/provisioning/profiles.py (oci_12gib)
Ubuntu 24.04 (aarch64)Host operating systemAlways Free eligible image for the A1 shapemanual/01-oci-host-and-network.md
LXD system containersRuns every lab machine (kali, meta, dvwa) and the infrastructure containers (gateway, Wazuh manager)System containers run a full Linux userspace (systemd, SSH, tmux, Docker inside dvwa) at a fraction of a VM's memory, which is what makes labs possible on 12 GiB. Instances are created from published golden images through pylxd.cyberrange/src/provisioning/provision.py, cyberrange/deploy/oci_build_goldens_arm64.sh
OVN (via LXD networks)Gives each lab its own virtual network pod-<id>-net (10.0.<50+id>.0/24) and peers it to the monitoring networkOne isolated layer-2 network per student lab, created and deleted by API calls. Peering lets Wazuh agents reach the manager without joining lab networks to each other.cyberrange/src/provisioning/pod_net.py, manual/02-gateway-and-wazuh.md
FastAPI (Python)The provisioning and scoring APIAsync request handling with background tasks for slow LXD work, Pydantic validation of every request body, dependency-injected auth guards (Depends)cyberrange/src/provisioning/provision_api_fastapi.py, cyberrange/src/provisioning/pods_router.py
SQLiteStores labs, scores, reviews, audit events, lab sessions, evidence image metadataSingle-host deployment with low write volume. The code uses WAL mode, a 30 s busy timeout and BEGIN IMMEDIATE transactions to handle concurrent writers. No separate database server to run on 12 GiB. Rationale partly inferred.cyberrange/src/provisioning/db.py, cyberrange/src/provisioning/migrate.py
Next.js 15 (App Router) + React 19The portal: all pages plus a server-side "backend for frontend" (BFF) API layerServer-side route handlers keep the FastAPI service off the public path and attach the user's token server-side. Edge middleware enforces login and role redirects before a page renders.cyberrange/portal/src/lib/apiProxy.ts, cyberrange/portal/src/middleware.ts
NextAuth v4OAuth/OIDC client and session management in the portalHandles the Keycloak authorization code flow with PKCE, encrypted session cookies and token refreshcyberrange/portal/src/lib/auth.ts
KeycloakIdentity provider: accounts, passwords, realm roles (student, instructor, admin)Standards-based OIDC login, central role management, token introspection for the API, and an admin REST API used by the Admin "Users" pagecyberrange/deploy/keycloak/keycloak.service, cyberrange/src/provisioning/keycloak_admin.py
Tailwind CSSPortal stylingUtility classes and shared design tokenscyberrange/portal/tailwind.config.js
xterm.jsIn-browser terminalA full terminal emulator in the page, so students need no SSH clientcyberrange/portal/src/components/terminal/XtermView.tsx
Python websockets + lxc execTerminal bridge from the browser to a container shellUses the LXD control plane instead of SSH, because the host has no network route into the per-lab OVN networkscyberrange/bridge-src/bridge.py, comment in cyberrange/src/provisioning/ssh_verifier.py
tmuxKeeps each student shell alive in a named session labReconnecting reattaches the same session, and Scenario 1's live Metasploit check reads that sessioncyberrange/bridge-src/bridge.py, cyberrange/src/provisioning/scoring_checks.sh
Bash scoring scriptMilestone checks run inside the lab containersChecks need to execute on the target (read shell history, inspect files, test a login), which a read-only file API cannot docyberrange/src/provisioning/scoring_checks.sh, header of cyberrange/src/provisioning/ssh_verifier.py
Wazuh 4.7.5 (manager only)SIEM: collects agent events from meta and dvwa, raises alerts used in Scenario 3 and in instructor viewsOn 12 GiB there is no room for the Wazuh indexer and dashboard, so only the manager runs; the portal reads alerts itselfmanual/02-gateway-and-wazuh.md, manual/09-known-issues.md (Issue 13), cyberrange/src/provisioning/alerts_reader.py
DVWA + MariaDB (Docker Compose inside dvwa)Deliberately vulnerable web application for Scenario 2A standard teaching target for SQL injection and XSS, pinned to security level Lowcyberrange/deploy/oci_build_goldens_arm64.sh, cyberrange/src/provisioning/dvwa_compose.py
nginxGateway reverse proxy in the guacamole containerSingle origin for the tunnel: portal, /auth (Keycloak) and the terminal WebSocket share one hostname, so cookies stay same-sitecyberrange/deploy/guacamole/nginx-ampere.conf, comment in cyberrange/portal/src/lib/auth.ts
Cloudflare Tunnel (cloudflared)Publishes the portal on HTTPS without any inbound firewall ruleThe tunnel dials out to Cloudflare, so ports 80 and 443 stay closed on the VMcyberrange/deploy/systemd/cloudflared.service, manual/01-oci-host-and-network.md
systemd user unitsRun the API, portal, bridge and release pullerRestart on failure, start at boot, environment files per servicecyberrange/deploy/systemd/
GitHub Actions + GitHub ReleasesBuilds the portal and packages the code tree; a "Promote" run publishes release ampere-liveThe host cannot build the portal itself, so it pulls a prebuilt .next and applies it with health checks and automatic rollback.github/workflows/ampere-release.yml, cyberrange/deploy/host/pull_release.sh

Decisions worth explaining to a panel ​

Why containers, not virtual machines? ​

The whole platform must fit in 12 GiB of RAM. A lab needs three machines. LXD system containers share the host kernel, so each lab machine costs hundreds of megabytes instead of gigabytes, yet it still behaves like a normal Linux server with services, users and a shell. Profiles cap each lab container: 2 CPUs and 2 GB for Kali, 1 CPU and 1 GB for meta and dvwa (manual/02-gateway-and-wazuh.md).

Why a separate network per lab? ​

Each lab gets its own OVN network, so one student's machines cannot see another student's machines. Lab networks are peered only to the monitoring network (mon-net) so Wazuh agents can report to the manager (cyberrange/src/provisioning/pod_net.py).

Why token introspection instead of verifying JWTs locally? ​

The API asks Keycloak about each token (results are cached for at most 60 seconds). A disabled account or a changed role therefore takes effect within a minute, and the Admin page clears the cache for that user immediately (cyberrange/src/provisioning/auth.py, cyberrange/src/provisioning/introspect_cache.py).

Why a backend-for-frontend layer? ​

The browser only ever talks to the portal. The portal's route handlers read the session server-side and forward the request with the bearer token, allow-listing query parameters. FastAPI is bound to a private address and is never exposed publicly (cyberrange/portal/src/lib/apiProxy.ts, cyberrange/deploy/systemd/cyberrange-provision-api.service).

Why lxc exec for the terminal and for scoring? ​

A code comment records that the earlier design (SSH from the host into each lab) always timed out because the host has no route into the per-lab OVN networks. The LXD control plane reaches every container without any network path (cyberrange/src/provisioning/ssh_verifier.py).

Needs verification

The repository does not record a written comparison against alternatives (for example Docker-only labs, Proxmox VMs or PostgreSQL). Rationale above comes from code comments and the manual. If the panel asks "what else did you consider?", confirm the answer with the team.

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