This document describes how to set up SSH session recording on a bastion host
using ob-session-recorder.
The session recorder captures all terminal I/O during SSH sessions, creating an audit trail for compliance and incident investigation.
User SSH → Bastion → [ob-session-recorder] → Backend Server
│
▼
/var/lib/open-bastion/sessions/
│
▼
(future: upload to LLNG)
The ob-session-recorder script is installed to /usr/sbin/ with the
PAM module package.
scriptcommand (fromutil-linux, usually pre-installed)jqfor JSON metadata generation- Optional:
asciinemafor asciinema format support - Optional:
ttyrecfor ttyrec format support - Optional:
uuidgenfor UUID generation (fallback uses /proc/sys/kernel/random/uuid)
# Debian/Ubuntu
apt-get install uuid-runtime jq
# RHEL/CentOS
dnf install util-linux jqCreate /etc/llng/session-recorder.conf:
# Directory where recordings are stored
# Structure: sessions_dir/<username>/<timestamp>_<session_id>.<format>
sessions_dir = /var/lib/open-bastion/sessions
# Recording format:
# script - Plain text typescript (default, always available)
# asciinema - JSON format, web-friendly (requires asciinema)
# ttyrec - Binary format, compact (requires ttyrec)
format = script
# Maximum session duration in seconds
# Sessions exceeding this limit are terminated
# Set to 0 to disable (not recommended)
max_duration = 86400Edit /etc/ssh/sshd_config to force all sessions through the recorder:
# Record all sessions except for emergency admin access
Match User *,!root,!admin
ForceCommand /usr/sbin/ob-session-recorder# Only record sessions for users in the "recorded" group
Match Group recorded
ForceCommand /usr/sbin/ob-session-recorder# Record all sessions (use with caution)
ForceCommand /usr/sbin/ob-session-recorderRestart SSH after changes:
systemctl restart sshd- Format: Plain text typescript
- Extension:
.typescript - Advantages: No dependencies, always available, standard Unix tool
- Replay:
cat recording.typescriptorscriptreplay
This is the default format because script is available on all systems.
- Format: JSON (asciinema v2)
- Extension:
.cast - Advantages: Web-friendly, can be replayed in browser, human-readable
- Replay:
asciinema play recording.castor web player
Requires asciinema package to be installed.
Example header:
{
"version": 2,
"width": 80,
"height": 24,
"timestamp": 1702742400,
"env": { "SHELL": "/bin/bash", "TERM": "xterm-256color" }
}- Format: Binary
- Extension:
.ttyrec - Advantages: Compact, efficient, standard format
- Replay:
ttyplay recording.ttyrec
Requires ttyrec package to be installed.
Each recording has an accompanying JSON metadata file (.json):
{
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"user": "dwho",
"client_ip": "192.168.1.100",
"tty": "/dev/pts/0",
"start_time": "2025-12-16T10:30:00Z",
"end_time": "2025-12-16T11:45:23Z",
"status": "completed",
"original_command": "",
"format": "asciinema",
"recording_file": "20251216-103000_550e8400-e29b-41d4-a716-446655440000.cast",
"hostname": "bastion.example.com",
"version": "0.1.0"
}| Field | Description |
|---|---|
session_id |
Unique UUID for the session |
user |
Unix username |
client_ip |
Client IP address (from SSH_CLIENT) |
tty |
TTY device |
start_time |
Session start (ISO 8601 UTC) |
end_time |
Session end (ISO 8601 UTC) |
status |
active, completed, or error:<code> |
original_command |
SSH_ORIGINAL_COMMAND if any |
format |
Recording format used |
recording_file |
Name of the recording file |
hostname |
Bastion hostname |
version |
Recorder version |
/var/lib/open-bastion/sessions/
├── dwho/
│ ├── 20251216-103000_550e8400-...-440000.cast
│ ├── 20251216-103000_550e8400-...-440000.json
│ ├── 20251216-143052_661f9511-...-551111.cast
│ └── 20251216-143052_661f9511-...-551111.json
├── rtyler/
│ └── ...
└── jsmith/
└── ...
- Permissions:
0700on directories,0600on files - Owner: The user who initiated the session
- Organization: One subdirectory per user
# Terminal replay
asciinema play /var/lib/open-bastion/sessions/dwho/20251216-103000_*.cast
# Or use the web player (future LLNG integration)ttyplay /var/lib/open-bastion/sessions/dwho/20251216-103000_*.ttyrec# View the raw typescript
cat /var/lib/open-bastion/sessions/dwho/20251216-103000_*.typescript
# Replay with timing (if timing file exists)
scriptreplay timing.txt recording.typescript- Session directory:
0700(user-owned) - Recording files:
0600(user-owned) - Config file:
0644(root-owned)
- Store recordings on encrypted filesystem if possible
- Consider log rotation and retention policies
- Sensitive data may be captured (passwords typed in terminals)
When uploading to LLNG (future feature):
- Use TLS for all transfers
- Authenticate with server token
- Consider bandwidth implications
# Look for session files
ls -la /var/lib/open-bastion/sessions/$USER/
# Check syslog
journalctl -t ob-session-recorder| Issue | Cause | Solution |
|---|---|---|
| No recording created | ForceCommand not active | Check sshd_config Match rules |
| Empty recording | Session ended immediately | Check for shell issues |
| Permission denied | Wrong directory permissions | chmod 700 /var/lib/open-bastion/sessions |
| Format not available | ttyrec not installed | Install ttyrec or use asciinema |
# Test the recorder manually
/usr/sbin/ob-session-recorder --help
# Check configuration
cat /etc/llng/session-recorder.conf| Variable | Description |
|---|---|
LLNG_RECORDER_CONFIG |
Config file path |
LLNG_SESSIONS_DIR |
Override sessions directory |
LLNG_RECORDER_FORMAT |
Override recording format |
LLNG_MAX_SESSION |
Override max session duration |
Future releases will support:
- Automatic upload of recordings to LLNG portal
- Session listing and search in LLNG Manager
- Web-based session replay
- Session annotations and bookmarks
See issues #17-20 in the project backlog.
- README.md - Main documentation
- Security Architecture - Security implementation details
- SECURITY.md - Security policy and reporting
- Bastion Architecture - Overall bastion design