- Neuer Collector: agent/collector/cron.go - Zwei Quellen: crontab -l (System) + config.xml OPNsense-Jobs - Daten im Heartbeat unter 'cron_jobs' - Pro Job: source, schedule, enabled, command, description, uuid, origin - Backend Model CronJob in models/system.go - Getestet: 18 Jobs auf Produktion (8 System + 10 OPNsense) - README + Dateistruktur aktualisiert - Agent auf allen 3 Firewalls deployed
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
Agent-Faehigkeiten (Status: Implementiert)
Datensammlung (automatisch bei jedem Heartbeat, alle 60s)
| Collector | Daten | Quelle |
|---|---|---|
| Hardware | Hersteller, Modell, Seriennummer, BIOS | kenv, sysctl |
| CPU | Modell, Cores, Threads, Frequenz, Auslastung | sysctl |
| Memory | Total, Used, Free | sysctl |
| Disks | ZFS Datasets, Belegung, Mountpoints | zfs list |
| Network | Interfaces, IPs, Status, MAC, Statistiken | ifconfig |
| Services | Laufende Dienste, Status | service -e, configctl |
| WireGuard | Tunnel, Peers, Transfer, Handshake | wg show |
| DHCP | Aktive Leases (KEA/ISC/dnsmasq) | Lease-Dateien, KEA JSON |
| Routing | Routing-Tabelle | netstat -rn |
| Gateways | Gateway-Status, RTT, Loss | configctl interface gateways status |
| Zertifikate | Alle Certs + CAs, Ablaufdatum, Aussteller | config.xml + PEM-Parsing |
| Plugins | Installierte OPNsense-Plugins | pkg info |
| Updates | Pending Core + Package Updates | opnsense-update -c, pkg upgrade -n |
| Cron | System-Crontab + OPNsense config.xml Jobs | crontab -l, config.xml |
Remote-Commands (via WebSocket, on-demand)
| Command | API-Endpoint | Beschreibung |
|---|---|---|
exec |
POST /agents/{id}/exec |
Beliebigen Shell-Befehl ausfuehren |
backup |
POST /agents/{id}/backup |
Config-Backup (/conf/config.xml) erstellen |
update_check |
POST /agents/{id}/update-check |
Verfuegbare Updates pruefen |
update |
POST /agents/{id}/update |
Normales Update (Core + Packages, opt. Reboot) |
major_update |
POST /agents/{id}/major-update |
Major-Upgrade (2-Phasen: Base/Kernel → Reboot → Packages) |
reboot |
POST /agents/{id}/reboot |
Reboot mit konfigurierbarer Verzoegerung |
wg_add_peer |
POST /agents/{id}/wireguard/peers |
WireGuard-Peer anlegen (Keypair + Config) |
wg_list_peers |
GET /agents/{id}/wireguard/peers |
Alle Peers einer WG-Instanz auflisten |
wg_delete_peer |
DELETE /agents/{id}/wireguard/peers/{uuid} |
WireGuard-Peer loeschen |
tunnel_connect |
POST /agents/{id}/tunnel |
TCP-Tunnel zu lokalem Dienst oeffnen |
tunnel_disconnect |
DELETE /agents/{id}/tunnel/{tid} |
Tunnel schliessen |
Registrierte Agents
| Agent-ID | Name | IP | Rolle |
|---|---|---|---|
6beb8dfd... |
OPN-PROD.intra.weidenhaupt.net | 192.168.85.1 | Produktion (READ-ONLY) |
e92e87a6... |
OPN-REP.intra.weidenhaupt.net | 192.168.85.33 | Test/Replikation |
1697d28e... |
OPN-TEST-UPDATE | 192.168.85.19 | Update-Tests |
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 |
Agent-Status
Status wird automatisch aus last_heartbeat berechnet (kein DB-Feld):
| Status | Bedingung |
|---|---|
online |
Heartbeat < 2 Minuten |
stale |
Heartbeat < 5 Minuten |
offline |
Heartbeat > 5 Minuten |
unknown |
Kein Heartbeat vorhanden |
GET /agents liefert ein status Feld pro Agent, GET /agents/{id} ebenfalls.
Agent-Events
| Methode | Endpoint | Beschreibung |
|---|---|---|
| GET | /api/v1/agents/events |
Alle Events (?limit=N&type=X) |
| GET | /api/v1/agents/{id}/events |
Events eines Agents (?limit=N&type=X) |
Event-Typen: online, offline, stale, connected, disconnected
Events werden automatisch geschrieben bei:
- WebSocket Connect/Disconnect (
connected/disconnected) - Inaktivitaets-Monitor erkennt Statuswechsel (
online/stale/offline)
# Alle Events (letzte 10)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/events?limit=10"
# Nur Offline-Events eines Agents
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/events?type=offline"
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 |
Metriken (TimescaleDB)
| Methode | Endpoint | Beschreibung |
|---|---|---|
| GET | /api/v1/agents/{id}/metrics |
Rohe Metriken abfragen (?metric=X&from=&to=&limit=N) |
| GET | /api/v1/agents/{id}/metrics/summary |
Aggregierte Metriken (?metric=X&bucket=5+minutes&from=&to=) |
Metriken werden automatisch bei jedem Heartbeat geschrieben (alle 60s):
cpu_usage, memory_used_percent, memory_used_bytes, memory_total_bytes,
uptime_seconds, disk_used_percent, disk_used_bytes, interface_rx_bytes,
interface_tx_bytes, gateway_rtt_ms, gateway_loss_percent
Retention: 90 Tage (Raw), Compression nach 7 Tagen.
Config-Backup
| Methode | Endpoint | Beschreibung |
|---|---|---|
| POST | /api/v1/agents/{id}/backup |
Backup jetzt ausloesen (via WebSocket) |
| GET | /api/v1/agents/{id}/backups |
Alle Backups auflisten (?limit=N) |
| GET | /api/v1/agents/{id}/backups/{id|latest} |
Backup herunterladen (?format=xml fuer Raw-XML) |
| DELETE | /api/v1/agents/{id}/backups/{id} |
Einzelnes Backup loeschen |
| GET | /api/v1/agents/{id}/backups/diff?a={id}&b={id} |
Zeilenweises Diff zwischen zwei Backups |
Backups werden dedupliziert: gleicher SHA256-Hash = kein neuer Eintrag. Config-XML wird Base64-kodiert vom Agent zum Backend uebertragen und als Klartext in SQLite gespeichert.
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-Beispiele
# SSH-Tunnel zur OPNsense
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/tunnel \
-d '{"target_host":"127.0.0.1","target_port":22}'
# Response: {"tunnel_id":"xxx","proxy_port":10000,...}
# Verbinden:
ssh -p 10000 root@192.168.85.13
# WebGUI-Tunnel (OPNsense auf Port 4444)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/tunnel \
-d '{"target_host":"127.0.0.1","target_port":4444}'
# Response: {"tunnel_id":"xxx","proxy_port":10001,...}
# Im Browser: https://192.168.85.13:10001/
# Aktive Tunnel auflisten
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/tunnels
# Tunnel schliessen
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X DELETE \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/tunnel/TUNNEL_ID
Zugangsdaten
Backend: https://192.168.85.13:8443
API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9
Agent-IDs:
6beb8dfd38ecf03eab6c0f21b4ac7bee OPN-PROD.intra.weidenhaupt.net (192.168.85.1, READ-ONLY)
e92e87a684b20db15d8d2344583c5887 OPN-REP.intra.weidenhaupt.net (192.168.85.33)
1697d28e57bc126534afc282b5de8ee0 OPN-TEST-UPDATE (192.168.85.19)
Agent-Beispiele
# Alle Agents auflisten (ID, Name, IP, Version, letzter Heartbeat)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents
# Einzelner Agent mit Systemdaten
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887
# Nur Systemdaten (Hardware, CPU, RAM, Disks, Services, etc.)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/system
# Agent loeschen
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X DELETE \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887
# Hub-Status (verbundene Agents, aktive Tunnel)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/hub/status
# Remote-Command ausfuehren
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/exec \
-d '{"command":"uptime","timeout":30}'
WireGuard-Peer Management
| Methode | Endpoint | Beschreibung |
|---|---|---|
| POST | /api/v1/agents/{id}/wireguard/peers |
Neuen Peer anlegen |
| GET | /api/v1/agents/{id}/wireguard/peers |
Alle Peers auflisten (?server_name=X) |
| DELETE | /api/v1/agents/{id}/wireguard/peers/{uuid} |
Peer loeschen |
Peer anlegen — Parameter:
| Parameter | Pflicht | Default | Beschreibung |
|---|---|---|---|
name |
Ja | — | Peer-Name (z.B. "VPN_MUELLER") |
allowed_ips |
Nein | 0.0.0.0/0 |
AllowedIPs fuer Client-Config |
dns |
Nein | — | DNS-Server fuer Client-Config |
server_name |
Nein | ALWAYSON_VPN |
Name der WG-Server-Instanz |
endpoint |
Nein | WAN-IP:Port | Oeffentlicher Endpoint (Auto-Detect ueber Default-Route) |
Ablauf beim Anlegen:
- Keypair wird auf der Firewall generiert (
wg genkey/wg pubkey) - Naechste freie IP aus dem Tunnel-Subnetz wird automatisch vergeben
- Peer wird in
config.xmleingetragen und dem Server zugewiesen configctl wireguard configurelaedt die Aenderung- Response enthaelt die komplette Client-Config (ready for import)
Beispiele:
# Peer anlegen (erster Peer bekommt .2, zweiter .3, etc.)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers \
-d '{"name":"VPN_MUELLER","allowed_ips":"0.0.0.0/0","dns":"10.172.100.210"}'
# Response:
# {
# "status": "ok",
# "data": {
# "peer_uuid": "abc123...",
# "name": "VPN_MUELLER",
# "client_ip": "10.33.144.2",
# "endpoint": "79.238.149.26:8080",
# "client_config": "[Interface]\nPrivateKey = ...\nAddress = 10.33.144.2/32\n..."
# }
# }
# Zweiten Peer anlegen (bekommt automatisch .3)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers \
-d '{"name":"VPN_SCHMIDT","allowed_ips":"0.0.0.0/0","dns":"10.172.100.210"}'
# Peer mit eigenem Endpoint (z.B. DynDNS)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers \
-d '{"name":"VPN_EXTERN","allowed_ips":"0.0.0.0/0","endpoint":"vpn.kunde.de:8080"}'
# Alle Peers auflisten
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers
# Peers einer bestimmten Instanz auflisten
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers?server_name=ALWAYSON_VPN"
# Peer loeschen (UUID aus der List-Response)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X DELETE \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/wireguard/peers/89c10e2d-148e-11f1-a7af-7c5a1c6c7b73
Metriken-Beispiele
# CPU-Auslastung der letzten 24h
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/metrics?metric=cpu_usage"
# RAM-Nutzung mit Zeitraum
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/metrics?metric=memory_used_percent&from=2026-02-28T00:00:00Z&to=2026-02-28T23:59:59Z"
# 5-Minuten-Durchschnitte der CPU
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/metrics/summary?metric=cpu_usage&bucket=5+minutes"
# Stuendliche RAM-Zusammenfassung
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/metrics/summary?metric=memory_used_percent&bucket=1+hour"
# Alle Metriken eines Agents (alle Typen, letzte 100)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/metrics?limit=100"
Backup-Beispiele
# Backup ausloesen (Produktion .1)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/backup
# Backup ausloesen (Test .33)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/backup
# Alle Backups auflisten
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
https://192.168.85.13:8443/api/v1/agents/6beb8dfd38ecf03eab6c0f21b4ac7bee/backups
# Neuestes Backup als XML herunterladen
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/backups/latest?format=xml" \
-o config-backup.xml
# Diff zwischen Backup 1 und 3
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" \
"https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/backups/diff?a=1&b=3"
Update-Beispiele
# Verfuegbare Updates pruefen
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/update-check
# Normales Update (ohne Reboot)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/update
# Normales Update mit Reboot
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/update \
-d '{"reboot":true}'
# Major-Upgrade auf Version 26.7
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/major-update \
-d '{"version":"26.7","reboot":true}'
# Reboot (5s Verzoegerung default)
curl -sk -H "X-API-Key: 01532e5a7c9e70bf2757df77a2f5b9b9" -X POST \
https://192.168.85.13:8443/api/v1/agents/e92e87a684b20db15d8d2344583c5887/reboot
Update-Ablauf
Normales Update (Patches innerhalb der gleichen Version):
opnsense-update— Core-Update herunterladenpkg upgrade -y— Paket-Updates installieren- Nachpruefung:
opnsense-update -c— Exit 1 = Reboot noetig
Response enthaelt reboot_required: true/false fuer das Frontend.
Major-Upgrade (z.B. 25.7 → 26.1):
opnsense-update -r <version> -bkp— Repository wechseln + Base + Kernel + Packages herunterladenpkg-static update -f— Repository-Katalog forciert aktualisierenpkg-static upgrade -y— Alle Pakete auf neue Version installieren- Reboot erforderlich (immer bei Major-Upgrade)
Major-Upgrades setzen reboot_required: true und reboot ist standardmaessig aktiviert.
WebSocket Commands (Backend → Agent)
// Remote-Befehl ausfuehren
{"type":"command","id":"cmd1","command":"exec","params":{"command":"uptime","timeout":30}}
// Update-Check (liefert core_update_available, pending_packages, reboot_required)
{"type":"command","id":"cmd2","command":"update_check"}
// Normales Update (reboot optional)
{"type":"command","id":"cmd3","command":"update","params":{"reboot":false}}
// Major-Update (reboot default true)
{"type":"command","id":"cmd4","command":"major_update","params":{"version":"26.1","reboot":true}}
// Reboot
{"type":"command","id":"cmd5","command":"reboot","params":{"delay":5}}
// Config-Backup (liest /conf/config.xml, liefert Base64 + SHA256 Hash)
{"type":"command","id":"cmd8","command":"backup","params":{}}
// 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)
│ │ └── cron.go # Cron-Jobs (System-Crontab + OPNsense config.xml)
│ └── ws/
│ ├── client.go # WebSocket Client, Reconnect, Ping/Pong
│ ├── handler.go # Command Handler (exec, backup, tunnel_connect/disconnect)
│ ├── wireguard.go # WireGuard Peer Management (add/delete/list)
│ └── tunnel.go # Session-basierter Tunnel Manager
├── backend/
│ ├── main.go # Entry, TLS, HTTP Server
│ ├── config/
│ │ └── config.go # YAML Config Loader
│ ├── db/
│ │ └── postgres.go # PostgreSQL + TimescaleDB (Agents, Backups, Metriken)
│ ├── models/
│ │ ├── agent.go # Agent, Register/Heartbeat Structs
│ │ └── system.go # SystemData, alle Collector-Structs
│ ├── monitor/
│ │ └── monitor.go # Inaktivitaets-Monitor (prueft Heartbeat, schreibt Events)
│ ├── api/
│ │ ├── handlers.go # REST API Handler (inkl. Agent-Status + Events)
│ │ ├── tunnel_proxy.go # Tunnel + Exec + Update REST Endpoints
│ │ ├── backup.go # Config-Backup Endpoints (trigger, list, download, diff)
│ │ ├── wireguard.go # WireGuard Peer API Endpoints
│ │ ├── metrics.go # Metriken-Abfrage Endpoints (raw + summary)
│ │ └── 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
- PostgreSQL + TimescaleDB: Agent-Registry, Config-Backups, Time-Series Metriken (pgx Driver)
- CGO-frei: Kein CGO noetig (pure-Go PostgreSQL Driver)
- 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.