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
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

This commit is contained in:
2025-12-17 23:59:17 -05:00
parent 05087aa380
commit 493668f746
110 changed files with 22054 additions and 24 deletions
+276
View File
@@ -0,0 +1,276 @@
# 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`