Skip to content

Admin Role & Workflow ​

Purpose ​

Admins operate the platform. They manage accounts, roles and lost authenticators in Keycloak, mark test accounts out of research data, watch service health and capacity, force-destroy stuck or abandoned labs, and read the audit log. Admins also have every instructor capability. Admins skip the first-login orientation and always land on /admin; opening /dashboard sends them back there (onboardingRedirectPath() and redirectPathForRole() in cyberrange/portal/src/lib/routeRoles.ts).

ExplainerThe admin1:08

Pages and navigation ​

Sidebar: Dashboard, Users, Pods, System, Audit log and Instructor view (adminNavItems in cyberrange/portal/src/lib/navigation.ts). When an admin is in the instructor view, the sidebar shows a Back to Admin link (cyberrange/portal/src/components/layout/Sidebar.tsx). /admin/* requires the admin role; /instructor/* accepts admins too (cyberrange/portal/src/lib/routeRoles.ts).

PageWhat it shows and doesBackend calls
/admin DashboardService Status (API, LXD, Keycloak, Wazuh), users and pods summary, Refresh, and the Knowledge-gain export with two admin-only options: identified usernames (anonymized is the default) and including test and staff accounts (pull request 272)GET /admin/infra-health, GET /instructor/export/knowledge-gain
/admin/users UsersKeycloak users with username, email, name, enabled flag, role and creation time. Search (server-side), plus role (All, Student, Instructor, Admin, No role) and status (Active, Disabled) filters that work on the loaded page and show "N of M users" (pull request 239). Row actions on every row except the admin's own: Enable / Disable and the role dropdown, and a More actions menu with Reset password, Reset MFA and, for student accounts, Exclude from reports… / Include in reports (pull request 274). Excluded students carry a "Not in reports" badge with the reason. After a create or password reset, a credential handoff dialog shows the username and password for at most 2 minutes so the admin can copy them to an approved channel; nothing is emailed (pull request 198). If user management is not set up on the host, the list and the Create dialog show a hint naming manual 04, Step 9 (UserAdminSetupHint)GET/POST /admin/users, PATCH /admin/users/{id}/enabled, PUT /admin/users/{id}/role, PUT /admin/users/{id}/password, DELETE /admin/users/{id}/mfa, PUT /admin/users/{id}/research-exclusion
/admin/pods PodsAll live labs; force-destroy with confirmation, then polling until the lab is goneGET /pods (admin sees all), DELETE /admin/pods/{id}/force-destroy
/admin/pods/[id] Pod detailOne lab's details; force-destroyGET /pods/{id}/status, DELETE /admin/pods/{id}/force-destroy
/admin/system SystemSystem health, pod capacity, host memory, service status, audit and telemetry informationGET /admin/infra-health
/admin/audit Audit logNewest-first audit events in category tabs (Admin actions, Lab activity, Scoring & reviews, All), with columns When, Event, By, Affected user, Result and Details (pull request 207), filters (event, result, affected user), Load older events and Download CSV of the filtered events, capped at the newest 5,000 (pull request 217)GET /admin/audit-log (with event_types for a tab)
/instructor/*Every instructor pageinstructor routes

Admin workflow ​

User management in detail ​

All user routes call _require_admin() first and talk to Keycloak's admin REST API through a dedicated service-account client (cyberrange-user-admin by default) that holds only the view-users, query-users and manage-users realm-management roles. The portal's own client secret is deliberately not reused for this (cyberrange/src/provisioning/keycloak_admin.py, cyberrange/deploy/host/setup_user_admin_client.sh).

RuleDetailSource
No self-registrationAccounts are only created here or by setup scriptsusers_router.py docstring
Username formatLowercase, ^[a-z0-9][a-z0-9_.-]{0,31}$, because the username becomes the student ID and part of container names (pod-<username>-kali)USERNAME_PATTERN
Single rolerole must be student, instructor or admin; setting a role replaces the previous app roleCreateUserRequest, SetRoleRequest
Password8 to 128 characters; temporary by default, so Keycloak forces a change at first login. Keycloak's realm policy (SEC-04) also rejects common passwords, the username, the email and (on reset) the last 3 passwords; a rejection comes back as 422 and leaves no half-built account (only a rejection at the password step is reported as a policy failure; later failures are audited with step=role or step=readback). The dialogs invite the admin to "Choose your own unique password or passphrase (8–128 characters)" and show an advisory strength meter with "Must" rows for length, username and email; Generate is optional and makes a 24-character digits-and-symbols password with Web Crypto. Password fields use autocomplete="new-password" and the name and email fields autocomplete="off", so the browser does not fill in the admin's own details (pull request 265). For these two routes the API's 422 body carries only fixed messages, never the rejected valueCreateUserRequest, SetPasswordRequest, CreateUserModal.tsx, ResetPasswordModal.tsx, PasswordStrengthMeter.tsx, lib/generatePassword.ts, sanitized_validation_error() in provision_api_fastapi.py
Reset MFADeletes only the user's otp credentials (never the password), ends their sessions and evicts cached tokens; the user re-enrols an authenticator at next sign-in. Idempotent (removed=0 when there was none); 409 on the admin's own accountremove_otp_credentials() in keycloak_admin.py, users_router.py
No hidden fieldsRequest models forbid extra fields, so raw Keycloak attributes cannot be smuggled inextra="forbid"
Self-protectionAn admin cannot disable, re-role, reset the password of or reset MFA on their own account (research exclusion is allowed on any account)_reject_self()
Research exclusionPUT /admin/users/{id}/research-exclusion with excluded and, when excluding, a reason (test_account, team_member or other). It writes a research_exclusions row keyed by username; no scores are deleted, and the account can be included again. Staff accounts are left out of the export automatically, so the page offers this only on student accounts (pull requests 273 and 301)users_router.py, research_exclusions.py, schema/v16.sql
Last-admin protectionThe last remaining admin cannot be demoted or disabled_reject_last_admin()
Actor re-checkThe acting admin's admin role is re-confirmed in Keycloak before a change_confirm_actor_still_admin()
Immediate revocationDisabling a user or changing a role clears that user's cached API tokens_revoke_cached_tokens() → introspect_cache.invalidate_user()
AuditADMIN_USER_CREATE, ADMIN_USER_ENABLE, ADMIN_USER_DISABLE, ADMIN_USER_ROLE_SET, ADMIN_USER_PASSWORD_RESET, ADMIN_USER_MFA_RESET, ADMIN_USER_RESEARCH_EXCLUDE, ADMIN_USER_RESEARCH_INCLUDE, each with the actor and result (OK, DENIED, FAILED)_audit()
Setup checkAt startup the API logs whether the user-admin client is configured; if not, it names setup_user_admin_client.sh and manual 04, Step 9 (warn-only; student flows are unaffected)log_configuration_status() in keycloak_admin.py

Lab and infrastructure management ​

  • See all labs: GET /pods returns every live lab for an admin (others see only their own).
  • Force-destroy: DELETE /admin/pods/{id}/force-destroy works on labs that are ACTIVE or FAILED_ROLLBACK_COMPLETE. The admin role check runs before anything is read, so a refused attempt leaves no trace; a permitted attempt writes ADMIN_POD_FORCE_DESTROY with the actor and the lab's previous status, then runs the normal teardown in the background (cyberrange/src/provisioning/pods_router.py).
  • System health: GET /admin/infra-health probes the API, LXD (including storage pool free space), Keycloak (an introspection probe) and Wazuh (the manager agent's status), each with a 2-second timeout, and reports Healthy, Degraded or Unavailable (cyberrange/src/provisioning/infra_health.py).
  • Audit log: GET /admin/audit-log is read-only (no route edits or deletes rows), filterable by one event type or a comma-separated event_types set (at most 64), student and result, paged by ID (up to 500 per page). The OK and Failed result filters cover outcomes rather than one exact value: OK matches OK and PASS, Failed matches FAILED, FAIL and ERROR, and both also match events whose result is empty but whose name ends in _OK/_SUCCESS or _FAIL/_FAILED (cyberrange/src/provisioning/audit_router.py). The CSV download pages through the same route in the browser (cyberrange/portal/src/lib/auditCsv.ts).
  • Research export: from the Admin dashboard an admin can download the knowledge-gain export with real usernames (anonymize=false) and with excluded test and staff accounts (include_excluded=true); instructors get 403 for both. Every export, allowed or denied, writes KNOWLEDGE_GAIN_EXPORTED with the actor, format, scope and counts, never student IDs (export_knowledge_gain() in pods_router.py).

Capability that exists only in the backend ​

Admin terminal access to any lab

The terminal bridge authorizes a WebSocket by calling GET /pods/{id}/status with the caller's token. That endpoint uses require_owner_or_admin, so an admin's token is accepted for any student's lab, including a shell on kali, meta or dvwa. No admin page links to this, but anyone holding an admin token could open it by building the WebSocket URL. The team should decide whether this is an intended support feature (and document it) or restrict the bridge to owners (cyberrange/bridge-src/bridge.py, cyberrange/src/provisioning/auth.py).

Allowed and restricted routes ​

AllowedRestricted
All admin and instructor pages, and /scenarios and /scenario/[id] for their own lab/dashboard and /orientation redirect to /admin (not a restriction, a landing rule)
All API routes: admin, instructor, self-scopedActing on their own account through user management (refused by design)
Status of any lab; force-destroy any labDestroying another user's lab through the student destroy route (owner-only; use force-destroy)

Data an admin can view or modify ​

DataViewModify
Keycloak users and rolesYesCreate, enable/disable, change role, reset password, reset MFA
Research exclusions (test accounts left out of the export)Yes (badge on the Users page)Exclude or include again
Knowledge-gain exportYes, anonymized or identifiedNo
All labsYesForce-destroy
System health, capacity, RAMYesNo (configuration is in files on the host)
Audit logYesNo (read-only by design)
Everything an instructor seesYesResolve reviews

Not implemented for admins ​

  • Deleting user accounts (disable instead).
  • Changing platform settings such as MAX_PODS, the per-scenario time limits (SCENARIO_TTL_MINUTES is in code) or profiles from the UI.
  • Changing the realm's MFA, password policy or lockout settings from the UI (host scripts only).
  • Managing scenarios, golden images, Wazuh rules or Keycloak clients from the UI.
  • Viewing or ending users' Keycloak sessions from the UI.

Sources

cyberrange/portal/src/app/admin/**, cyberrange/portal/src/components/admin/*, cyberrange/portal/src/lib/navigation.ts, lib/adminUserFilter.ts, lib/auditEvents.ts, lib/auditCsv.ts, components/instructor/KnowledgeGainExport.tsx, cyberrange/src/provisioning/users_router.py, research_exclusions.py, keycloak_admin.py, pods_router.py, infra_health.py, audit_router.py, auth.py, cyberrange/bridge-src/bridge.py.

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