rmm2/README.md
cynfo3000 ebc70f17eb Update-Commands: update, major_update, update_check, reboot
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
2026-02-28 09:01:04 +01:00

298 lines
11 KiB
Markdown

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