8.0 KiB
SSH Web Terminal Feature
This document describes the SSH web terminal feature integrated into the Homelab Dashboard.
Overview
The SSH Web Terminal allows users to connect to bootstrapped hosts directly from the web interface using ttyd as the terminal emulator backend.
Features
- Embedded Terminal: Opens in a slide-over drawer panel within the Hosts page
- Pop-out Terminal: Opens in a standalone window (or new tab if popups are blocked)
- Session Management: Automatic expiration, session limits per user
- Security: Token-based authentication, no SSH keys exposed to browser
Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web Browser │────▶│ FastAPI │────▶│ ttyd Process │
│ (iframe) │ │ Backend │ │ (per session) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ ▼
│ ┌─────────────────┐
│ │ SSH Client │
│ │ automation@ │
│ │ <host> │
│ └─────────────────┘
▼
┌─────────────────┐
│ SQLite DB │
│ (sessions) │
└─────────────────┘
Prerequisites
1. Install ttyd
Linux (Debian/Ubuntu)
sudo apt-get install ttyd
Linux (from source)
git clone https://github.com/tsl0922/ttyd.git
cd ttyd && mkdir build && cd build
cmake ..
make && sudo make install
macOS
brew install ttyd
Windows (WSL)
sudo apt-get install ttyd
Docker
ttyd can also be run via Docker, but the current implementation spawns ttyd processes directly.
2. SSH Key Configuration
Hosts must be bootstrapped with the automation user and SSH key configured. The bootstrap process:
- Creates the
automationuser on the target host - Deploys SSH public key for passwordless authentication
- Configures sudo without password for the automation user
3. Environment Variables
| Variable | Default | Description |
|---|---|---|
TERMINAL_SESSION_TTL_MINUTES |
30 |
Session timeout in minutes |
TERMINAL_MAX_SESSIONS_PER_USER |
3 |
Maximum concurrent sessions per user |
TERMINAL_PORT_RANGE_START |
7680 |
First port for ttyd instances |
TERMINAL_PORT_RANGE_END |
7700 |
Last port for ttyd instances |
TERMINAL_SSH_USER |
automation |
SSH username for connections |
TTYD_PATH |
ttyd |
Path to ttyd binary |
API Endpoints
Check Terminal Feature Status
GET /api/terminal/status
Returns whether ttyd is installed and terminal feature is available.
Response:
{
"available": true,
"ttyd_installed": true,
"max_sessions_per_user": 3,
"session_ttl_minutes": 30,
"active_sessions": 2
}
Create Terminal Session
POST /api/terminal/{host_id}/terminal-sessions
Authorization: Bearer <token>
Content-Type: application/json
{
"mode": "embedded" // or "popout"
}
Response:
{
"session_id": "abc123...",
"url": "/terminal/connect/abc123...?token=xyz...",
"websocket_url": "ws://localhost:7680/ws",
"expires_at": "2024-12-17T20:00:00Z",
"ttl_seconds": 1800,
"mode": "embedded",
"host": {
"id": "host-id",
"name": "server01",
"ip": "192.168.1.100",
"status": "online",
"bootstrap_ok": true
}
}
List Active Sessions
GET /api/terminal/sessions
Authorization: Bearer <token>
Close Terminal Session
DELETE /api/terminal/sessions/{session_id}
Authorization: Bearer <token>
Connect to Terminal
GET /api/terminal/connect/{session_id}?token=<session_token>
Returns HTML page with embedded terminal iframe.
Security Considerations
Authentication
- All API endpoints require JWT Bearer token authentication
- Session tokens are generated per-session and hashed before storage
- Token verification on every terminal page load
Session Isolation
- Each terminal session spawns a dedicated ttyd process
- ttyd uses
--onceflag to exit after client disconnects - Sessions are bound to specific users
Rate Limiting
- Maximum sessions per user (default: 3)
- Session TTL prevents resource exhaustion
- Expired sessions are automatically cleaned up
Audit Logging
- Session creation logged with user and host info
- Session closure logged
- No sensitive data (tokens, keys) in logs
SSH Security
- SSH keys never exposed to browser
StrictHostKeyChecking=accept-newfor initial connections- Uses dedicated
automationuser with limited sudo
UI Components
Terminal Button (Host Card)
- Enabled: When host is online AND bootstrap OK
- Disabled: When host is offline OR bootstrap not completed
- Shows tooltip explaining why disabled
Terminal Drawer
- Slides in from right side of screen
- Header: Host name, IP, connection status
- Body: Embedded ttyd iframe
- Footer: SSH command copy, reconnect, session timer
- Close with Escape key or X button
Pop-out Window
- Opens in new window with minimal UI
- Fullscreen terminal experience
- PWA hint for toolbar-free experience
Troubleshooting
"ttyd is not installed"
Install ttyd using the instructions above. Verify with:
which ttyd
ttyd --version
"Host not bootstrapped"
Run bootstrap on the host first:
- Go to Hosts section
- Click "Bootstrap" button on the target host
- Enter root password when prompted
- Wait for bootstrap to complete
"Maximum sessions reached"
Close existing terminal sessions before opening new ones.
Pop-up blocked
If the browser blocks the pop-out window, the terminal will open in a new tab instead.
Session expired
Click "Reconnect" button to create a new session.
PWA / Toolbar-free Window
Modern browsers don't allow complete removal of the URL bar via window.open(). To get a true "app-like" experience:
Option 1: Chrome/Edge App Mode
Create a shortcut with:
chrome --app=https://your-dashboard.com/terminal/connect/SESSION_ID?token=TOKEN
Option 2: Install as PWA
- Open the dashboard in Chrome/Edge
- Click the install icon in the address bar
- Launch terminals from the installed PWA
Database Schema
CREATE TABLE terminal_sessions (
id VARCHAR(64) PRIMARY KEY,
host_id VARCHAR NOT NULL,
host_name VARCHAR NOT NULL,
host_ip VARCHAR NOT NULL,
user_id VARCHAR,
username VARCHAR,
token_hash VARCHAR(128) NOT NULL,
ttyd_port INTEGER NOT NULL,
ttyd_pid INTEGER,
mode VARCHAR(20) DEFAULT 'embedded',
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
closed_at TIMESTAMP WITH TIME ZONE
);
Files
Backend
app/models/terminal_session.py- SQLAlchemy modelapp/schemas/terminal.py- Pydantic schemasapp/crud/terminal_session.py- Database operationsapp/services/terminal_service.py- ttyd process managementapp/routes/terminal.py- API endpoints
Frontend
app/main.js- Terminal methods in DashboardManagerapp/index.html- CSS styles for terminal drawer
Migration
alembic/versions/0013_add_terminal_sessions_table.py
Tests
tests/backend/test_terminal.py