Agent:
- update: opnsense-update + pkg upgrade -y, optionaler Reboot
- major_update: opnsense-update -r <version>, optionaler Reboot
- update_check: Prueft Core- und Paket-Updates, liefert Details
- reboot: Shutdown mit konfigurierbarer Verzoegerung
Backend:
- Synchrone API-Endpoints (warten auf Agent-Response)
- Response-Waiter-System im Hub (Command-ID basiert)
- Konfigurierbare Timeouts pro Command-Typ
- POST /agents/{id}/update[-check] und /major-update und /reboot
README mit Update-Beispielen und API-Doku aktualisiert
OPNsense RMM System
Remote Monitoring & Management fuer OPNsense Firewalls. Go-basierter Agent + Backend mit WebSocket-Kommunikation und Session-basierten TCP-Tunneln.
Architektur
┌────────────────┐ HTTPS/WSS ┌────────────────┐ TCP Proxy ┌────────────┐
│ OPNsense FW │◄──────────────►│ RMM Backend │◄───────────────│ Browser/ │
│ (FreeBSD) │ :8443 │ (Linux) │ :10000-20000 │ SSH/etc. │
│ │ │ │ └────────────┘
│ rmm-agent │ Heartbeat │ REST API │
│ - Collectors │ WebSocket │ WebSocket Hub │
│ - WS Client │ Binary Msgs │ Tunnel Mgr │
│ - Tunnel Mgr │ │ SQLite DB │
└────────────────┘ └────────────────┘
Tunnel-Flow
Client ──► Backend:ProxyPort ──► WebSocket (Binary) ──► Agent ──► Zieldienst
(TCP-Listener) (Session-basiert) (TCP) (SSH/HTTPS/etc)
- Jeder Tunnel oeffnet einen dynamischen Proxy-Port (10000-20000) auf dem Backend
- Jede TCP-Verbindung am Proxy bekommt eine eigene Session
- Der Agent oeffnet pro Session eine separate Ziel-Verbindung (Lazy Connect)
- Mehrere gleichzeitige Verbindungen pro Tunnel moeglich
- Tunnel bleiben offen wenn einzelne Sessions beendet werden
Komponenten
Agent (FreeBSD/OPNsense)
- Collectors: Hardware, CPU, Memory, Disk, Network, Services, WireGuard, DHCP (KEA/ISC/dnsmasq), Routing, Gateways, Zertifikate, Plugins, Updates
- WebSocket Client: Persistente Verbindung zum Backend, automatisches Reconnect mit Backoff
- Command Handler: Remote-Befehlsausfuehrung, Tunnel-Connect/Disconnect
- Tunnel Manager: Session-basierte TCP-Verbindungen zu lokalen Diensten
Backend (Linux)
- REST API: Agent-Registrierung, Heartbeat, Systemdaten, Tunnel-Management
- WebSocket Hub: Verwaltet Agent-Verbindungen, routet Commands und Binary-Messages
- Tunnel Manager: Proxy-Listener, Session-Tracking, Ready-Handshake
- SQLite DB: Persistente Agent- und Systemdaten
Build
make all # Backend (linux/amd64) + Agent (freebsd/amd64)
make backend # Nur Backend
make agent # Nur Agent
Binaries in build/. Benoetigt Go 1.22+.
Konfiguration
Backend (config.yaml)
listen_addr: ":8443"
tls_cert: "certs/server.crt" # Wird automatisch generiert
tls_key: "certs/server.key"
db_path: "rmm.db"
api_keys:
- "dein-api-key-hier"
Agent (config.yaml)
backend_url: "https://backend-ip:8443"
api_key: "dein-api-key-hier"
interval_seconds: 60
agent_name: "opnsense-fw01"
insecure: true # Self-signed Certs akzeptieren
Deployment
Backend
# Build + Copy
make backend
scp build/rmm-backend user@backend:/pfad/rmm-backend
# Starten (als normaler User, kein root noetig)
cd /pfad && nohup ./rmm-backend config.yaml > rmm-backend.log 2>&1 &
Agent (OPNsense/FreeBSD)
# Build + Copy
make agent
scp build/rmm-agent root@opnsense:/usr/local/rmm/rmm-agent
# Starten via FreeBSD daemon
daemon -f -p /var/run/rmm-agent.pid -o /usr/local/rmm/rmm-agent.log \
/usr/local/rmm/rmm-agent --config /usr/local/rmm/config.yaml --insecure
Wichtig: Agent-ID wird persistent in /usr/local/rmm/agent_id gespeichert.
API
Agent-Management
| Methode | Endpoint | Beschreibung |
|---|---|---|
| POST | /api/v1/agent/register |
Agent registrieren (mit optionaler agent_id) |
| POST | /api/v1/agent/heartbeat |
Systemdaten senden |
| GET | /api/v1/agents |
Alle Agents auflisten |
| GET | /api/v1/agents/{id} |
Agent + Systemdaten abrufen |
| GET | /api/v1/agents/{id}/system |
Nur Systemdaten |
| DELETE | /api/v1/agents/{id} |
Agent loeschen |
WebSocket
| Methode | Endpoint | Beschreibung |
|---|---|---|
| GET | /api/v1/agent/ws?agent_id=X&api_key=X |
WebSocket-Verbindung (Agent) |
| GET | /api/v1/hub/status |
Hub-Status (connected agents, aktive Tunnel) |
Updates & Reboot
| Methode | Endpoint | Beschreibung |
|---|---|---|
| POST | /api/v1/agents/{id}/update-check |
Verfuegbare Updates pruefen |
| POST | /api/v1/agents/{id}/update |
Normales Update (Core + Packages) |
| POST | /api/v1/agents/{id}/major-update |
Major-Upgrade auf Zielversion |
| POST | /api/v1/agents/{id}/reboot |
Reboot ausfuehren |
Tunnel-Management
| Methode | Endpoint | Beschreibung |
|---|---|---|
| POST | /api/v1/agents/{id}/tunnel |
Tunnel oeffnen |
| GET | /api/v1/agents/{id}/tunnels |
Aktive Tunnel auflisten |
| DELETE | /api/v1/agents/{id}/tunnel/{tid} |
Tunnel schliessen |
| POST | /api/v1/agents/{id}/exec |
Remote-Command ausfuehren |
Tunnel erstellen
# SSH-Tunnel zur OPNsense
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/tunnel \
-d '{"target_host":"127.0.0.1","target_port":22}'
# Response: {"tunnel_id":"xxx","proxy_port":10000,...}
# Verbinden:
ssh -p 10000 root@backend-ip
# WebGUI-Tunnel (OPNsense auf Port 4444)
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/tunnel \
-d '{"target_host":"127.0.0.1","target_port":4444}'
# Response: {"tunnel_id":"xxx","proxy_port":10001,...}
# Im Browser: https://backend-ip:10001/
Update-Beispiele
# Verfuegbare Updates pruefen
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/update-check
# Normales Update (ohne Reboot)
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/update
# Normales Update mit Reboot
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/update \
-d '{"reboot":true}'
# Major-Upgrade auf Version 26.7
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/major-update \
-d '{"version":"26.7","reboot":true}'
# Reboot (5s Verzoegerung default)
curl -sk -H "X-API-Key: KEY" -X POST \
https://backend:8443/api/v1/agents/AGENT_ID/reboot
WebSocket Commands (Backend → Agent)
// Remote-Befehl ausfuehren
{"type":"command","id":"cmd1","command":"exec","params":{"command":"uptime","timeout":30}}
// Update-Check
{"type":"command","id":"cmd2","command":"update_check"}
// Normales Update
{"type":"command","id":"cmd3","command":"update","params":{"reboot":false}}
// Major-Update
{"type":"command","id":"cmd4","command":"major_update","params":{"version":"26.7","reboot":true}}
// Reboot
{"type":"command","id":"cmd5","command":"reboot","params":{"delay":5}}
// Tunnel-Session oeffnen (automatisch vom Backend gesendet)
{"type":"command","id":"cmd6","command":"tunnel_connect","params":{"session_id":"xxx","tunnel_id":"xxx","target_host":"127.0.0.1","target_port":22}}
// Tunnel-Session schliessen
{"type":"command","id":"cmd7","command":"tunnel_disconnect","params":{"session_id":"xxx"}}
Binary WebSocket Messages (Tunnel-Daten)
Format: [session_id_length:1][session_id][tcp_payload]
Tunnel-Daten werden als Binary WebSocket Messages uebertragen (kein Base64-Overhead).
Dateistruktur
rmm/
├── Makefile
├── README.md
├── agent/
│ ├── main.go # Entry, Registration, Heartbeat-Loop, WS-Start
│ ├── config/
│ │ └── config.go # YAML Config Loader
│ ├── client/
│ │ └── client.go # HTTP Client (Register, Heartbeat)
│ ├── collector/
│ │ ├── hardware.go # Hersteller, Modell, BIOS
│ │ ├── cpu.go # CPU-Modell, Cores, Usage
│ │ ├── memory.go # RAM total/used/free
│ │ ├── disk.go # ZFS Datasets, Belegung
│ │ ├── network.go # Interfaces, IPs, Status
│ │ ├── services.go # Laufende Dienste
│ │ ├── wireguard.go # WG-Tunnel, Peers, Transfer
│ │ ├── dhcp.go # KEA/ISC/dnsmasq Leases
│ │ ├── opnsense.go # OPN-Version, FreeBSD, Hostname, Uptime
│ │ ├── routing.go # Routing-Tabelle (netstat -rn)
│ │ ├── gateways.go # Gateway-Status (configctl)
│ │ ├── certificates.go # Zertifikate + CAs (config.xml + PEM)
│ │ ├── plugins.go # Installierte Plugins (pkg info)
│ │ └── updates.go # Pending Updates (core + packages)
│ └── ws/
│ ├── client.go # WebSocket Client, Reconnect, Ping/Pong
│ ├── handler.go # Command Handler (exec, tunnel_connect/disconnect)
│ └── tunnel.go # Session-basierter Tunnel Manager
├── backend/
│ ├── main.go # Entry, TLS, HTTP Server
│ ├── config/
│ │ └── config.go # YAML Config Loader
│ ├── db/
│ │ └── sqlite.go # SQLite (Agent + Systemdaten)
│ ├── models/
│ │ ├── agent.go # Agent, Register/Heartbeat Structs
│ │ └── system.go # SystemData, alle Collector-Structs
│ ├── api/
│ │ ├── handlers.go # REST API Handler
│ │ ├── tunnel_proxy.go # Tunnel + Exec REST Endpoints
│ │ └── middleware.go # API-Key Auth, Logging
│ └── ws/
│ ├── handler.go # WebSocket Upgrade, Connection R/W Pumps
│ ├── hub.go # Agent-Registry, Message Routing
│ └── tunnel.go # Session-basierter Tunnel + Proxy Manager
└── build/ # Kompilierte Binaries
Technische Details
- CGO-frei:
CGO_ENABLED=0, pure-Go SQLite (modernc.org/sqlite) - Cross-Compilation: Backend linux/amd64, Agent freebsd/amd64
- TLS: Self-signed Zertifikate (automatisch generiert)
- WebSocket: gorilla/websocket, Binary Messages fuer Tunnel-Daten
- Persistente Agent-ID: Ueberlebt Agent- und Backend-Neustarts
- FreeBSD-kompatibel: Volle Pfade (/usr/bin/netstat etc.), daemon statt nohup
Hinweise
- Backend braucht kein root — laeuft als normaler User auf Port 8443
- Agent braucht root auf OPNsense (fuer Hardware-/Service-Daten)
- OPNsense WebGUI typischerweise auf Port 4444 (nicht 443)
- FreeBSD csh hat kein
$()— Agent verwendet/bin/sh -cfuer Commands - Beim Backend-Deploy: alte Prozesse killen bevor neuer startet (sonst "bind: address already in use")
Lizenz
Intern.
Description
Languages
JavaScript
47.7%
Go
47.3%
Shell
3.2%
PHP
0.6%
Makefile
0.4%
Other
0.6%