# 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` | ### 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 ```bash 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`) ```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`) ```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 ```bash # 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) ```bash # 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`) ```bash # 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 ```bash # 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 ```bash # 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:** ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 -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) ```json // 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) │ └── 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 -c` fuer Commands - Beim Backend-Deploy: **alte Prozesse killen** bevor neuer startet (sonst "bind: address already in use") ## Lizenz Intern.