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
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:
@@ -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`
|
||||
Reference in New Issue
Block a user