Skip to content

Emergency Console

Why an emergency console

When an alert tells you “something weird is running on host X”, you usually want to look right now. Installing SSH/RDP everywhere = wide attack surface, ongoing maintenance, and no forensic trail. The emergency console opens a short-lived, scoped shell over the existing agent WS tunnel — no inbound ports, no permanent credentials on the host, complete server-side audit trail.

Auth flow (identical on every OS)

  1. Operator clicks Open Emergency Console on an agent.
  2. Dashboard asks for reason (≥20 chars) + TOTP code.
  3. Hub API validates role (admin/owner), TOTP, and rate-limit (3 sessions / hour / user).
  4. Hub signs an Ed25519 console-session token (15 min TTL, nonce, agent-bound) and pushes a console_start frame to the agent over the existing WebSocket.
  5. Agent verifies signature + nonce + TTL, spawns a local shell, opens a keystroke stream back to the hub.
  6. Browser opens wss://api.monsys.ai/ws/console/<session_id> — bidirectional binary stream.

Every I/O byte is written to console_audit_log on the hub (direction=‘I’ for input, ‘O’ for output) with sequence_num and agent_id. At session end a SHA256 of the full log (in sequence_num order) is pinned on console_sessions.audit_hash as forensic proof.

Linux side — bash --restricted as monsys-console

At agent install:

  • useradd -r -s /bin/false -d /var/lib/monsys/console monsys-console
  • .bashrc with red [MONSYS-CONSOLE] prompt + HISTTIMEFORMAT

Per session:

Terminal window
setpriv --reuid=monsys-console --regid=monsys-console \
--init-groups --inh-caps=-all \
-- /bin/bash --restricted \
--rcfile /var/lib/monsys/console/.bashrc

Restricted bash blocks cd, output redirection with >, and PATH= rewrites. Caps are stripped. The sudoers fragment for monsys-console is empty → no privilege escalation possible.

Windows side — ConPTY + PowerShell with JEA

On Windows, a pseudo-console (ConPTY, native API since Win10 1809) gives real terminal semantics (colors, resize, ctrl-c), and Just Enough Administration (JEA) enforces the cmdlet whitelist server-side.

At agent install (install.ps1):

  • Creates module MonsysJEA under $Env:ProgramFiles\WindowsPowerShell\Modules\
  • Writes Role Capability File MonsysConsole.psrc with the whitelist
  • Writes Session Configuration File monsys-console-jea.pssc with RunAsVirtualAccount=$true, LanguageMode=NoLanguage, SessionType=RestrictedRemoteServer
  • Register-PSSessionConfiguration -Name monsys-console-jea

Per session:

powershell.exe -NoLogo -NoProfile -NoExit \
-ConfigurationName monsys-console-jea

Stdin/stdout of the ConPTY pair is mapped onto the WS stream.

What the operator CAN do (whitelist)

Forensic-IR set, ~60 cmdlets:

CategoryCmdlets
ProcessGet-Process, Stop-Process, Wait-Process, Debug-Process
ServiceGet/Stop/Start/Restart/Suspend/Resume-Service, Set-Service
Event logGet-EventLog, Get-WinEvent (no Clear-EventLog)
NetworkGet-NetTCPConnection, Get-NetUDPEndpoint, Get-NetAdapter, Get-NetIPAddress, Get-NetIPConfiguration, Get-NetRoute, Get-DnsClient*, Resolve-DnsName, Test-Connection, Test-NetConnection
ContainmentDisable/Enable-NetAdapter, New/Remove/Get-NetFirewallRule, Set-NetFirewallProfile
UsersGet-LocalUser, Get-LocalGroup, Get-LocalGroupMember (no Add/Set/Remove)
FilesystemGet-ChildItem, Get-Item, Get-Content, Get-ItemProperty, Get-FileHash, Test-Path, Resolve-Path, Get-Acl, Get-AuthenticodeSignature
TasksGet-ScheduledTask, Disable-ScheduledTask
WMI/CIMGet-CimInstance, Get-CimClass (no Invoke-CimMethod)
PerfGet-Counter, Get-ComputerInfo, Get-HotFix
PipelineForEach-Object, Where-Object, Select-Object, Sort-Object, Group-Object, Measure-Object, Compare-Object, Format-*, Out-*, Select-String
PowerRestart-Computer, Stop-Computer

What the operator CANNOT do (JEA blocks server-side)

  • Invoke-Expression, Invoke-Command — no arbitrary code execution
  • New-LocalUser, Add-LocalGroupMember, Set-LocalUser — no new privileged accounts
  • Remove-Item, Format-Volume — no destruction
  • Clear-EventLog — no forensic-evidence tampering
  • Set-Acl on critical paths — no permission changes
  • .exe executables outside the whitelist
  • New-PSSession — no pivoting to other hosts
  • Scripts or variable expansion (LanguageMode=NoLanguage) — only direct cmdlet invocations

Try a blocked cmdlet, get a server-side red error:

The term 'Remove-Item' is not recognized as the name of a cmdlet, function, ...

JEA transcripts are also written locally to $Env:ProgramData\monsys\console-transcripts\ as a secondary forensic trail. The primary trail stays console_audit_log on the hub.

Session end

Session ends on:

  • Browser disconnect (operator closes tab)
  • 15 min TTL reached → auto-close
  • exit / Exit-PSSession
  • Tenant suspended → hub closes all open sessions immediately

At every close a SHA256 of the full console_audit_log (in sequence_num order) is computed and pinned on the session row. That hash can be compared later to prove the log was not altered.

Permissions summary

LinuxWindows
Shellbash --restrictedpowershell (LanguageMode=NoLanguage)
User contextmonsys-console (low-priv)JEA RunAs virtual account (ephemeral)
WhitelistRestricted bash pathJEA RoleCapability (~60 cmdlets)
Filesystem destructionBlocked (no redirects)Remove-Item not in whitelist
Privilege escalationNo sudoers entry for monsys-consoleVirtual account, not in admin group
Audit (hub)console_audit_log with SHA256 sealsame
Audit (host fallback)n/a$ProgramData\monsys\console-transcripts\