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  │                │  PostgreSQL +  │
│                │                │  TimescaleDB   │
└────────────────┘                └────────────────┘

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
  • PostgreSQL + TimescaleDB: Relationale Daten + Time-Series Metriken

Voraussetzungen

  • Build-System: Go 1.24+ (Cross-Compilation, kein CGO)
  • Backend-Server: Linux (Debian 12+), PostgreSQL 17+, TimescaleDB 2.x
  • Agent-Ziel: OPNsense / FreeBSD (amd64)

Installation (Backend)

1. PostgreSQL + TimescaleDB installieren

# Debian 12+ (Bookworm/Trixie)
apt install -y postgresql postgresql-client

# TimescaleDB Repository hinzufuegen
echo "deb https://packagecloud.io/timescale/timescaledb/debian/ bookworm main" \
  > /etc/apt/sources.list.d/timescaledb.list
curl -L https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor \
  > /etc/apt/trusted.gpg.d/timescaledb.gpg
apt update

# TimescaleDB passend zur PG-Version installieren (z.B. PG 17)
apt install -y timescaledb-2-postgresql-17

# TimescaleDB in postgresql.conf aktivieren
echo "shared_preload_libraries = 'timescaledb'" >> /etc/postgresql/17/main/conf.d/timescaledb.conf
systemctl restart postgresql

2. Datenbank und User anlegen

sudo -u postgres psql <<EOF
CREATE USER rmm WITH PASSWORD 'SICHERES_PASSWORT';
CREATE DATABASE rmm OWNER rmm;
\c rmm
CREATE EXTENSION IF NOT EXISTS timescaledb;
GRANT ALL ON SCHEMA public TO rmm;
EOF

Verbindung testen:

psql -h 127.0.0.1 -U rmm -d rmm -c "SELECT default_version FROM pg_available_extensions WHERE name='timescaledb';"

3. Build

make all       # Backend (linux/amd64) + Agent (freebsd/amd64)
make backend   # Nur Backend
make agent     # Nur Agent

Binaries landen in build/. Kein CGO — reine Go-Compilation.

4. TLS-Zertifikate generieren

make certs

Oder manuell:

mkdir -p certs
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
  -keyout certs/server.key -out certs/server.crt \
  -days 3650 -nodes \
  -subj "/CN=rmm-backend/O=RMM" \
  -addext "subjectAltName=IP:BACKEND_IP,IP:127.0.0.1,DNS:localhost"

5. Backend konfigurieren (config.yaml)

listen_addr: ":8443"
tls_cert: "certs/server.crt"
tls_key: "certs/server.key"
api_keys:
  - "SICHERER_API_KEY"

database:
  host: "127.0.0.1"
  port: 5432
  user: "rmm"
  password: "SICHERES_PASSWORT"
  dbname: "rmm"
  sslmode: "disable"        # "require" wenn PG ueber Netz

Alternativ per Environment-Variablen: RMM_LISTEN_ADDR, RMM_TLS_CERT, RMM_TLS_KEY, RMM_DB_HOST, RMM_DB_PASSWORD.

6. Backend deployen und starten

# Verzeichnis anlegen
ssh root@BACKEND_IP "mkdir -p /opt/rmm/certs"

# Binary + Config + Certs kopieren
scp build/rmm-backend root@BACKEND_IP:/opt/rmm/rmm-backend
scp config.yaml root@BACKEND_IP:/opt/rmm/config.yaml
scp certs/server.* root@BACKEND_IP:/opt/rmm/certs/

# Systemd-Service einrichten
ssh root@BACKEND_IP 'cat > /etc/systemd/system/rmm-backend.service << EOF
[Unit]
Description=RMM Backend
After=network.target postgresql.service

[Service]
Type=simple
WorkingDirectory=/opt/rmm
ExecStart=/opt/rmm/rmm-backend /opt/rmm/config.yaml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF'

ssh root@BACKEND_IP "systemctl daemon-reload && systemctl enable rmm-backend && systemctl start rmm-backend"

Oder per Makefile: make deploy-backend

7. Starten und verifizieren

# Status pruefen
systemctl status rmm-backend

# Log pruefen
journalctl -u rmm-backend -f

# API testen
curl -sk -H "X-API-Key: DEIN_API_KEY" https://BACKEND_IP:8443/api/v1/agents

Beim ersten Start:

  • Tabellen agents, system_data, config_backups, agent_events werden automatisch angelegt
  • Hypertable metrics wird erstellt (TimescaleDB)
  • Retention Policy (90 Tage) und Compression Policy (nach 7 Tagen) werden automatisch gesetzt

8. Datenbank-Schema (automatisch)

Das Backend erstellt folgendes Schema bei erstem Start:

Tabelle Typ Beschreibung
agents Relational Agent-Registry (ID, Name, Hostname, IP, Version, Heartbeat)
system_data Relational Letzte Systemdaten pro Agent (JSONB)
config_backups Relational OPNsense Config-Backups (dedupliziert per SHA256)
agent_events Relational Online/Offline/Connected Events
metrics TimescaleDB Hypertable Time-Series Metriken (CPU, RAM, Disk, Network, Gateway)

TimescaleDB Policies:

  • Retention: Rohdaten werden nach 90 Tagen automatisch geloescht
  • Compression: Daten aelter als 7 Tage werden komprimiert (segmentby: agent_id+metric)

Installation (Agent / OPNsense)

1. Agent bauen

make agent    # Erzeugt build/rmm-agent (freebsd/amd64)

2. Agent konfigurieren (agent/config.yaml)

backend_url: "https://BACKEND_IP:8443"
api_key: "SICHERER_API_KEY"
interval_seconds: 60
agent_name: "opnsense-fw01"
insecure: true                  # Self-signed Certs akzeptieren (false bei CA-Certs)

3. Agent deployen

# Verzeichnis anlegen + Binary + Config kopieren
ssh root@OPNSENSE "mkdir -p /usr/local/rmm"
scp build/rmm-agent root@OPNSENSE:/usr/local/rmm/rmm-agent
scp agent/config.yaml root@OPNSENSE:/usr/local/rmm/config.yaml

# Starten via FreeBSD daemon
ssh root@OPNSENSE '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'

Oder per Makefile: make deploy-agent

4. Autostart einrichten (rc.d)

ssh root@OPNSENSE 'cat > /usr/local/etc/rc.d/rmm_agent << RCEOF
#!/bin/sh
# PROVIDE: rmm_agent
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name="rmm_agent"
rcvar="rmm_agent_enable"
pidfile="/var/run/${name}.pid"
command="/usr/local/rmm/rmm-agent"
command_args="--config /usr/local/rmm/config.yaml --insecure &"

load_rc_config $name
run_rc_command "$1"
RCEOF
chmod +x /usr/local/etc/rc.d/rmm_agent'

ssh root@OPNSENSE 'sysrc rmm_agent_enable="YES" && service rmm_agent restart'

5. Verifizieren

# Agent-Log pruefen
ssh root@OPNSENSE "tail -20 /usr/local/rmm/rmm-agent.log"

# Auf dem Backend: Agent sollte erscheinen
curl -sk -H "X-API-Key: DEIN_API_KEY" https://BACKEND_IP:8443/api/v1/agents

Wichtig: Agent-ID wird persistent in /usr/local/rmm/agent_id gespeichert. Bei Neuinstallation bleibt die ID erhalten.

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 PostgreSQL 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:

  1. Keypair wird auf der Firewall generiert (wg genkey/wg pubkey)
  2. Naechste freie IP aus dem Tunnel-Subnetz wird automatisch vergeben
  3. Peer wird in config.xml eingetragen und dem Server zugewiesen
  4. configctl wireguard configure laedt die Aenderung
  5. 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):

  1. opnsense-update — Core-Update herunterladen
  2. pkg upgrade -y — Paket-Updates installieren
  3. 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):

  1. opnsense-update -r <version> -bkp — Repository wechseln + Base + Kernel + Packages herunterladen
  2. pkg-static update -f — Repository-Katalog forciert aktualisieren
  3. pkg-static upgrade -y — Alle Pakete auf neue Version installieren
  4. 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 17 + TimescaleDB 2.x: Agent-Registry, Config-Backups, Time-Series Metriken
  • Pure-Go: pgx/v5 Driver, kein CGO noetig
  • 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 -c fuer Commands
  • Beim Backend-Deploy: alte Prozesse killen bevor neuer startet (sonst "bind: address already in use")

Lizenz

Intern.

Description
cynfo RMM — Backend, Agent, Frontend, OPNsense-Plugin (clean history)
Readme 26 MiB
Languages
JavaScript 47.7%
Go 47.3%
Shell 3.2%
PHP 0.6%
Makefile 0.4%
Other 0.6%