Bruno Charest 493668f746
Some checks failed
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Add comprehensive SSH terminal drawer feature with embedded and popout modes, integrate playbook lint results API with local cache fallback, and enhance host management UI with terminal access buttons
2025-12-17 23:59:17 -05:00

277 lines
8.0 KiB
Markdown

# 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](https://github.com/tsl0922/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)
```bash
sudo apt-get install ttyd
```
#### Linux (from source)
```bash
git clone https://github.com/tsl0922/ttyd.git
cd ttyd && mkdir build && cd build
cmake ..
make && sudo make install
```
#### macOS
```bash
brew install ttyd
```
#### Windows (WSL)
```bash
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:
1. Creates the `automation` user on the target host
2. Deploys SSH public key for passwordless authentication
3. 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
```http
GET /api/terminal/status
```
Returns whether ttyd is installed and terminal feature is available.
**Response:**
```json
{
"available": true,
"ttyd_installed": true,
"max_sessions_per_user": 3,
"session_ttl_minutes": 30,
"active_sessions": 2
}
```
### Create Terminal Session
```http
POST /api/terminal/{host_id}/terminal-sessions
Authorization: Bearer <token>
Content-Type: application/json
{
"mode": "embedded" // or "popout"
}
```
**Response:**
```json
{
"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
```http
GET /api/terminal/sessions
Authorization: Bearer <token>
```
### Close Terminal Session
```http
DELETE /api/terminal/sessions/{session_id}
Authorization: Bearer <token>
```
### Connect to Terminal
```http
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 `--once` flag 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-new` for initial connections
- Uses dedicated `automation` user 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:
```bash
which ttyd
ttyd --version
```
### "Host not bootstrapped"
Run bootstrap on the host first:
1. Go to Hosts section
2. Click "Bootstrap" button on the target host
3. Enter root password when prompted
4. 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:
```bash
chrome --app=https://your-dashboard.com/terminal/connect/SESSION_ID?token=TOKEN
```
### Option 2: Install as PWA
1. Open the dashboard in Chrome/Edge
2. Click the install icon in the address bar
3. Launch terminals from the installed PWA
## Database Schema
```sql
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 model
- `app/schemas/terminal.py` - Pydantic schemas
- `app/crud/terminal_session.py` - Database operations
- `app/services/terminal_service.py` - ttyd process management
- `app/routes/terminal.py` - API endpoints
### Frontend
- `app/main.js` - Terminal methods in DashboardManager
- `app/index.html` - CSS styles for terminal drawer
### Migration
- `alembic/versions/0013_add_terminal_sessions_table.py`
### Tests
- `tests/backend/test_terminal.py`