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