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)
- Operator clicks Open Emergency Console on an agent.
- Dashboard asks for reason (≥20 chars) + TOTP code.
- Hub API validates role (admin/owner), TOTP, and rate-limit (3 sessions / hour / user).
- Hub signs an Ed25519 console-session token (15 min TTL, nonce, agent-bound) and pushes a
console_startframe to the agent over the existing WebSocket. - Agent verifies signature + nonce + TTL, spawns a local shell, opens a keystroke stream back to the hub.
- 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.bashrcwith red[MONSYS-CONSOLE]prompt +HISTTIMEFORMAT
Per session:
setpriv --reuid=monsys-console --regid=monsys-console \ --init-groups --inh-caps=-all \ -- /bin/bash --restricted \ --rcfile /var/lib/monsys/console/.bashrcRestricted 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
MonsysJEAunder$Env:ProgramFiles\WindowsPowerShell\Modules\ - Writes Role Capability File
MonsysConsole.psrcwith the whitelist - Writes Session Configuration File
monsys-console-jea.psscwithRunAsVirtualAccount=$true,LanguageMode=NoLanguage,SessionType=RestrictedRemoteServer Register-PSSessionConfiguration -Name monsys-console-jea
Per session:
powershell.exe -NoLogo -NoProfile -NoExit \ -ConfigurationName monsys-console-jeaStdin/stdout of the ConPTY pair is mapped onto the WS stream.
What the operator CAN do (whitelist)
Forensic-IR set, ~60 cmdlets:
| Category | Cmdlets |
|---|---|
| Process | Get-Process, Stop-Process, Wait-Process, Debug-Process |
| Service | Get/Stop/Start/Restart/Suspend/Resume-Service, Set-Service |
| Event log | Get-EventLog, Get-WinEvent (no Clear-EventLog) |
| Network | Get-NetTCPConnection, Get-NetUDPEndpoint, Get-NetAdapter, Get-NetIPAddress, Get-NetIPConfiguration, Get-NetRoute, Get-DnsClient*, Resolve-DnsName, Test-Connection, Test-NetConnection |
| Containment | Disable/Enable-NetAdapter, New/Remove/Get-NetFirewallRule, Set-NetFirewallProfile |
| Users | Get-LocalUser, Get-LocalGroup, Get-LocalGroupMember (no Add/Set/Remove) |
| Filesystem | Get-ChildItem, Get-Item, Get-Content, Get-ItemProperty, Get-FileHash, Test-Path, Resolve-Path, Get-Acl, Get-AuthenticodeSignature |
| Tasks | Get-ScheduledTask, Disable-ScheduledTask |
| WMI/CIM | Get-CimInstance, Get-CimClass (no Invoke-CimMethod) |
| Perf | Get-Counter, Get-ComputerInfo, Get-HotFix |
| Pipeline | ForEach-Object, Where-Object, Select-Object, Sort-Object, Group-Object, Measure-Object, Compare-Object, Format-*, Out-*, Select-String |
| Power | Restart-Computer, Stop-Computer |
What the operator CANNOT do (JEA blocks server-side)
Invoke-Expression,Invoke-Command— no arbitrary code executionNew-LocalUser,Add-LocalGroupMember,Set-LocalUser— no new privileged accountsRemove-Item,Format-Volume— no destructionClear-EventLog— no forensic-evidence tamperingSet-Aclon critical paths — no permission changes.exeexecutables outside the whitelistNew-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
| Linux | Windows | |
|---|---|---|
| Shell | bash --restricted | powershell (LanguageMode=NoLanguage) |
| User context | monsys-console (low-priv) | JEA RunAs virtual account (ephemeral) |
| Whitelist | Restricted bash path | JEA RoleCapability (~60 cmdlets) |
| Filesystem destruction | Blocked (no redirects) | Remove-Item not in whitelist |
| Privilege escalation | No sudoers entry for monsys-console | Virtual account, not in admin group |
| Audit (hub) | console_audit_log with SHA256 seal | same |
| Audit (host fallback) | n/a | $ProgramData\monsys\console-transcripts\ |