- 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
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
# 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)
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)
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)
make deploy-backend
Dies startet:
- REST API Server auf :8443
- WebSocket Hub für Agent-Verbindungen
- Tunnel Manager für TCP-Proxies
- SQLite-Datenbank für persistente Daten
Agent (192.168.85.33 / OPNsense)
make deploy-agent
Dies startet:
- HTTP Heartbeat-Loop (weiterhin alle 60s)
- WebSocket-Client mit automatischem Reconnect
- Command-Handler für eingehende Befehle
- Tunnel-Manager für TCP-Verbindungen
API-Dokumentation
WebSocket Commands (Backend → Agent)
exec - Befehl ausführen
{
"type": "command",
"id": "cmd-123",
"command": "exec",
"params": {
"command": "ps aux | head -10",
"timeout": 30
}
}
Response:
{
"type": "response",
"id": "cmd-123",
"status": "ok",
"data": {
"output": "PID COMMAND\n1 kernel\n..."
}
}
tunnel_open - TCP-Tunnel öffnen
{
"type": "command",
"id": "cmd-124",
"command": "tunnel_open",
"params": {
"target_host": "127.0.0.1",
"target_port": 443
}
}
Response:
{
"type": "response",
"id": "cmd-124",
"status": "ok",
"data": {
"tunnel_id": "a1b2c3d4e5f6789a"
}
}
tunnel_close - TCP-Tunnel schließen
{
"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:
{
"command": "uptime",
"timeout": 30
}
Response (200):
{
"message": "Command gesendet",
"command_id": "cmd-123"
}
POST /api/v1/agents/{id}/tunnel
TCP-Tunnel zu Agent öffnen.
Request:
{
"target_host": "127.0.0.1",
"target_port": 443
}
Response (201):
{
"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):
{
"message": "Tunnel geschlossen"
}
GET /api/v1/agents/{id}/tunnels
Aktive Tunnel auflisten.
Response (200):
[
{
"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):
{
"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
-
Tunnel öffnen:
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}' -
Über Tunnel auf OPNsense WebUI zugreifen:
# Backend allokiert Port 12345 für Tunnel # Browser kann jetzt zugreifen über: https://192.168.85.13:12345/ -
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.