rmm2/README.md
cynfo3000 f47154787d WebSocket-Infrastruktur: bidirektionaler Command-Kanal + TCP-Tunnel
- Agent: WS-Client mit Reconnect, Command-Handler (exec, tunnel_open/close/data)
- Backend: WS-Hub, Tunnel-Manager mit dynamischen Proxy-Ports (10000-20000)
- API: /agents/{id}/exec, /tunnel, /tunnels, /proxy/{tunnel_id}
- Binary WebSocket Messages fuer effiziente Tunnel-Daten
- gorilla/websocket (pure Go, kein CGO)
- README aktualisiert
2026-02-28 07:42:33 +01:00

381 lines
9.4 KiB
Markdown

# OPNsense RMM System
Remote Monitoring & Management System fuer OPNsense Firewalls mit WebSocket-basierter Kommunikation und TCP-Tunnel-Funktionalitaet.
## Ueberblick
Dieses System besteht aus zwei Komponenten:
- **Backend** (Go) - REST API Server mit WebSocket Hub, TLS und SQLite-Datenbank, laeuft auf einem Linux-Server
- **Agent** (Go) - Laeuft auf OPNsense/FreeBSD, sammelt Systemdaten, haelt WebSocket-Verbindung zum Backend und ermoeglicht Remote-Commands/Tunneling
## Architektur
```
┌──────────────────────┐ WebSocket + HTTPS (TLS) ┌──────────────────────┐
│ OPNsense FW │ ◄─────────────────────────────► │ RMM Backend │
│ (FreeBSD) │ wss://backend:8443/api/v1/ │ (Linux) │
│ │ agent/ws │ │
│ rmm-agent │ │ REST API │
│ - Hardware/CPU/RAM │ POST /agent/register │ WebSocket Hub │
│ - Disk/Network │ POST /agent/heartbeat │ Tunnel Manager │
│ - Services/Certs │ │ SQLite DB │
│ - WebSocket Client │ Commands: │ :8443 │
│ - Tunnel Manager │ • exec (Befehl ausfuehren) │ │
│ │ • tunnel_open/close │ 192.168.85.13 │
│ 192.168.85.33 │ • tunnel_data (TCP-Proxy) │ │
└──────────────────────┘ └──────────────────────┘
TCP-Tunnel Flow:
Browser/Client ──► Backend Proxy ──► WebSocket ──► Agent ──► OPNsense Service
(Port 10000-20000) (Binary) (Local) (z.B. :443, :22)
```
## Neue Funktionen (WebSocket/Tunnel)
### 1. Bidirektionale Kommunikation
- **WebSocket-Verbindung**: Agent baut nach Registrierung eine persistente WebSocket-Verbindung zum Backend auf
- **Automatisches Reconnect**: Bei Verbindungsabbruch mit exponential backoff
- **Ping/Pong Keepalive**: Verbindung wird automatisch überwacht
- **Command-Response-Pattern**: Backend kann Commands an Agents senden und Responses erhalten
### 2. Remote Command Execution
- **exec Command**: Backend kann Befehle auf der OPNsense ausführen lassen
- **Timeout-Support**: Configurable timeouts für Command-Execution
- **stdout/stderr Output**: Vollständige Ausgabe wird zurückgeliefert
### 3. TCP-Tunneling über WebSocket
- **tunnel_open**: Agent öffnet TCP-Verbindung zu lokalem Service (z.B. OPNsense WebUI :443)
- **Dynamische Proxy-Ports**: Backend allokiert automatisch freie Ports (10000-20000)
- **Binary WebSocket Messages**: Effiziente Datenübertragung ohne Base64-Overhead
- **Multi-Tunnel Support**: Mehrere gleichzeitige Tunnel pro Agent möglich
### 4. HTTP-Proxy durch Tunnel
- **Reverse Proxy**: `GET /api/v1/agents/{id}/proxy/{tunnel_id}/*` leitet HTTP-Traffic durch Tunnel
- **Transparent**: Browser kann direkt auf OPNsense WebUI zugreifen über Backend-URL
## Voraussetzungen
- Go 1.22+
- make
- OpenSSL (fuer manuelle Zertifikat-Generierung)
- SSH-Zugang zu den Zielservern
- gorilla/websocket Library (automatisch über go mod)
## Build
```bash
# Beide Komponenten bauen
make all
# Nur Backend (linux/amd64)
make backend
# Nur Agent (freebsd/amd64)
make agent
# Dependencies aktualisieren
cd agent && go mod tidy
cd backend && go mod tidy
# TLS-Zertifikate generieren (optional, Backend generiert automatisch)
make certs
```
Die Binaries landen in `build/`.
## Konfiguration
### Backend (`backend/config.yaml`)
```yaml
listen_addr: ":8443"
tls_cert: "certs/server.crt"
tls_key: "certs/server.key"
db_path: "rmm.db"
api_keys:
- "01532e5a7c9e70bf2757df77a2f5b9b9"
```
**Neue WebSocket-Features:**
- WebSocket-Hub läuft automatisch
- Tunnel-Manager verwaltet aktive Tunnel
- Port-Range 10000-20000 für dynamische Proxy-Ports
### Agent (`agent/config.yaml`)
```yaml
backend_url: "https://192.168.85.13:8443"
api_key: "01532e5a7c9e70bf2757df77a2f5b9b9"
interval_seconds: 60
agent_name: "opnsense-fw01"
insecure: true
```
**WebSocket wird automatisch gestartet:**
- WebSocket URL: `wss://192.168.85.13:8443/api/v1/agent/ws?agent_id=XXX&api_key=XXX`
- Reconnect mit Backoff bei Verbindungsabbruch
- Ping/Pong Keepalive alle 30s
## Deployment
### Backend (192.168.85.13)
```bash
make deploy-backend
```
Dies startet:
1. REST API Server auf :8443
2. WebSocket Hub für Agent-Verbindungen
3. Tunnel Manager für TCP-Proxies
4. SQLite-Datenbank für persistente Daten
### Agent (192.168.85.33 / OPNsense)
```bash
make deploy-agent
```
Dies startet:
1. HTTP Heartbeat-Loop (weiterhin alle 60s)
2. WebSocket-Client mit automatischem Reconnect
3. Command-Handler für eingehende Befehle
4. Tunnel-Manager für TCP-Verbindungen
## API-Dokumentation
### WebSocket Commands (Backend → Agent)
#### exec - Befehl ausführen
```json
{
"type": "command",
"id": "cmd-123",
"command": "exec",
"params": {
"command": "ps aux | head -10",
"timeout": 30
}
}
```
**Response:**
```json
{
"type": "response",
"id": "cmd-123",
"status": "ok",
"data": {
"output": "PID COMMAND\n1 kernel\n..."
}
}
```
#### tunnel_open - TCP-Tunnel öffnen
```json
{
"type": "command",
"id": "cmd-124",
"command": "tunnel_open",
"params": {
"target_host": "127.0.0.1",
"target_port": 443
}
}
```
**Response:**
```json
{
"type": "response",
"id": "cmd-124",
"status": "ok",
"data": {
"tunnel_id": "a1b2c3d4e5f6789a"
}
}
```
#### tunnel_close - TCP-Tunnel schließen
```json
{
"type": "command",
"id": "cmd-125",
"command": "tunnel_close",
"params": {
"tunnel_id": "a1b2c3d4e5f6789a"
}
}
```
### Neue REST API Endpoints
#### POST /api/v1/agents/{id}/exec
Befehl auf Agent ausführen.
**Request:**
```json
{
"command": "uptime",
"timeout": 30
}
```
**Response (200):**
```json
{
"message": "Command gesendet",
"command_id": "cmd-123"
}
```
#### POST /api/v1/agents/{id}/tunnel
TCP-Tunnel zu Agent öffnen.
**Request:**
```json
{
"target_host": "127.0.0.1",
"target_port": 443
}
```
**Response (201):**
```json
{
"tunnel_id": "a1b2c3d4e5f6789a",
"proxy_port": 12345,
"target_host": "127.0.0.1",
"target_port": 443,
"created_at": "2024-01-28T12:30:00Z"
}
```
#### DELETE /api/v1/agents/{id}/tunnel/{tunnel_id}
Tunnel schließen.
**Response (200):**
```json
{
"message": "Tunnel geschlossen"
}
```
#### GET /api/v1/agents/{id}/tunnels
Aktive Tunnel auflisten.
**Response (200):**
```json
[
{
"tunnel_id": "a1b2c3d4e5f6789a",
"proxy_port": 12345,
"target_host": "127.0.0.1",
"target_port": 443,
"active": true,
"created_at": "2024-01-28T12:30:00Z"
}
]
```
#### GET /api/v1/agents/{id}/proxy/{tunnel_id}/*
HTTP-Reverse-Proxy durch Tunnel.
**Beispiel:**
```
GET /api/v1/agents/abc123/proxy/tunnel789/
→ Leitet weiter an OPNsense WebUI (127.0.0.1:443) über Tunnel
```
#### GET /api/v1/hub/status
WebSocket Hub Status.
**Response (200):**
```json
{
"status": "running",
"connected_agents": ["abc123", "def456"],
"stats": {
"connected_agents": 2,
"active_tunnels": 3
},
"timestamp": "2024-01-28T12:30:00Z"
}
```
### WebSocket Endpoint
#### GET /api/v1/agent/ws
WebSocket-Upgrade für Agents.
**Query Parameters:**
- `agent_id`: Agent-ID (aus Registrierung)
- `api_key`: API-Key für Authentifizierung
**Usage:**
```
wss://192.168.85.13:8443/api/v1/agent/ws?agent_id=abc123&api_key=xxx
```
## Tunnel-Datenformat
### Binary WebSocket Messages
TCP-Tunnel-Daten werden als Binary Messages übertragen:
**Format:** `[tunnel_id_length:1][tunnel_id][data]`
- Byte 0: Länge der Tunnel-ID (max 255)
- Bytes 1-N: Tunnel-ID als UTF-8 String
- Bytes N+1-Ende: TCP-Payload-Daten
### Beispiel-Usage
1. **Tunnel öffnen:**
```bash
curl -X POST http://backend:8443/api/v1/agents/abc123/tunnel \
-H "X-API-Key: xxx" \
-d '{"target_host":"127.0.0.1","target_port":443}'
```
2. **Über Tunnel auf OPNsense WebUI zugreifen:**
```
# Backend allokiert Port 12345 für Tunnel
# Browser kann jetzt zugreifen über:
https://192.168.85.13:12345/
```
3. **Oder über HTTP-Proxy:**
```
https://192.168.85.13:8443/api/v1/agents/abc123/proxy/tunnel789/
```
## Technische Details
### CGO-frei
- Beide Komponenten kompilieren mit `CGO_ENABLED=0`
- Agent läuft auf FreeBSD/amd64 (OPNsense)
- Backend läuft auf Linux/amd64
- gorilla/websocket ist pure Go, kein CGO erforderlich
### Sicherheit
- TLS-verschlüsselte WebSocket-Verbindungen (WSS)
- API-Key-Authentifizierung für alle Endpoints
- Agent validiert Commands vor Ausführung
- Tunnel sind isoliert per Agent/Tunnel-ID
### Performance
- Binary WebSocket Messages für Tunnel-Daten (kein Base64-Overhead)
- Connection-Pooling für gleichzeitige Tunnel
- Effiziente Port-Allokation für Proxies
- Heartbeat läuft weiterhin parallel (für Monitoring auch ohne WS)
### Monitoring
- Agent funktioniert auch ohne WebSocket (nur Heartbeat)
- WebSocket-Verbindungsstatus wird überwacht
- Inactive Tunnel werden automatisch bereinigt
- Hub-Status zeigt Connected Agents und aktive Tunnel
## Lizenz
Intern.