Browser SSH Session Recording
Status
Implemented. Branch feature/ssh-session-recording. Closes the recording deferral
noted in OP-086.
Related Requirements
- Taskboard OP-D089; client requirement "Browser SSH: recording, command logging, JIT, RBAC, timeouts" (VN-02).
Problem Statement
The browser-SSH broker stored recording metadata only: recording_ref pointed
nowhere real and recording_sha256 was a hash of session_id:command_count:ended_at
— not of an actual recorded stream. So "session recording" was over-claimed.
Architectural Intent
The gateway already terminates SSH server-side and relays the PTY byte stream, so the honest place to capture a recording is the relay itself. Capture the output stream there, persist it at session end with a real content hash, and expose a tenant-scoped retrieval endpoint. No agent or client change.
What Was Implemented
SshTerminatorcaptures the PTY output stream (_pump_to_browser) into a buffer — a faithful server-side transcript (the PTY echoes keystrokes into output).- On gateway teardown,
end_ssh_session_from_gateway(..., recording=...)persists the transcript (base64) to a newssh_session_recordingstable and setsrecording_sha256to the realsha256of the captured bytes. GET /api/v1/rmm/ssh-sessions/{session_id}/recordingreturns the transcript + hash to the operator who owns the session, or an auditor/tenant-admin/system-admin.
Components Involved
poc/ingest_api/ssh_terminator.py(capture),ssh_gateway.py(teardown handoff)poc/ingest_api/ssh_broker_service.py(_persist_ssh_recording,read_ssh_recording)db/postgres/021_ssh_session_recordings.sql+ sqliteinit_db+ RLSpoc/ingest_api/http_routes.py,specs/openapi.yaml
APIs / Events / Schemas
- New:
GET /api/v1/rmm/ssh-sessions/{session_id}/recording(OpenAPI updated). - New table
ssh_session_recordings(RLS, tenant-scoped). No event-schema change; thesession.ssh.endedpayload now carries a realrecording_sha256.
Deployment Notes
Run Postgres migration 021_ssh_session_recordings.sql (wired into the Makefile and
compose db-migrate). No agent change.
Security / Tenant Isolation
Recording rows are tenant-scoped (RLS); retrieval requires read role and, for an operator, ownership of the session. Auditors may read (read-only evidence access).
Validation Steps
API Validation
# after a browser SSH session ends:
GET /api/v1/rmm/ssh-sessions/{session_id}/recording
# -> { recording_sha256, byte_len, transcript_b64 };
# base64-decode transcript_b64, sha256 it, and confirm it equals recording_sha256
Smoke / Tests
tests/test_ssh_session_recording.py: capture→persist→real hash, content-not-metadata,
tenant isolation + operator-owner scope + auditor read.
Known Limitations
- Captures the output stream (server-side typescript), not a timed asciicast replay.
- Transcript is stored in the tenant-scoped table in the clear (POC); envelope encryption via the per-tenant key (VN-12) is the contract-designed follow-up.
Follow-Up Work
- xterm replay UI; timed/asciicast capture; envelope encryption at rest.
Acceptance Criteria Mapping
OP-D089 / VN-02 "recording" — a browser SSH session is recorded server-side and retrievable with a content hash that verifies the actual session. Satisfied by the capture + persistence + retrieval above and the four DoD tests.