773 lines
30 KiB
Markdown
773 lines
30 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 │ │ 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
psql -h 127.0.0.1 -U rmm -d rmm -c "SELECT default_version FROM pg_available_extensions WHERE name='timescaledb';"
|
|
```
|
|
|
|
### 3. Build
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
make certs
|
|
```
|
|
|
|
Oder manuell:
|
|
|
|
```bash
|
|
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`)
|
|
|
|
```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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
Der Agent wird als OPNsense-Plugin installiert — mit WebGUI-Konfiguration unter **Services > RMM Agent**.
|
|
|
|
### 1. Agent bauen
|
|
|
|
```bash
|
|
make agent # Erzeugt build/rmm-agent (freebsd/amd64)
|
|
```
|
|
|
|
### 2. Plugin installieren
|
|
|
|
```bash
|
|
# Binary + Installer auf die Firewall kopieren
|
|
scp build/rmm-agent root@OPNSENSE:/tmp/
|
|
scp opnsense-plugin/install.sh root@OPNSENSE:/tmp/
|
|
|
|
# Installer ausfuehren
|
|
ssh root@OPNSENSE '/bin/sh /tmp/install.sh'
|
|
```
|
|
|
|
Der Installer richtet alles ein:
|
|
- Binary nach `/usr/local/rmm/rmm-agent`
|
|
- rc.d Service (`/usr/local/etc/rc.d/rmm_agent`)
|
|
- OPNsense MVC Plugin (Model, Controller, View)
|
|
- configd Actions (start/stop/restart/status/reconfigure)
|
|
- Plugin-Hook fuer Service-Integration
|
|
- Setup-Script das Config aus dem Model generiert
|
|
|
|
### 3. WebGUI konfigurieren
|
|
|
|
1. OPNsense WebGUI oeffnen
|
|
2. **Services > RMM Agent**
|
|
3. Felder ausfuellen:
|
|
- **Aktiviert**: Ankreuzen
|
|
- **Backend URL**: `https://BACKEND_IP:8443`
|
|
- **API Key**: Backend API-Key eintragen
|
|
- **Agent Name**: Optional (Standard: Hostname)
|
|
- **Heartbeat Intervall**: 60 (Sekunden)
|
|
- **Self-Signed Zertifikate**: Ankreuzen bei self-signed Backend-Certs
|
|
4. **Speichern & Anwenden** klicken
|
|
|
|
Der Agent startet automatisch und meldet sich am Backend an.
|
|
|
|
### 4. Verifizieren
|
|
|
|
```bash
|
|
# Status in der WebGUI: Badge zeigt "Running"
|
|
|
|
# Oder per SSH:
|
|
ssh root@OPNSENSE 'service rmm_agent status'
|
|
|
|
# Auf dem Backend: Agent sollte erscheinen
|
|
curl -sk -H "X-API-Key: DEIN_API_KEY" https://BACKEND_IP:8443/api/v1/agents
|
|
```
|
|
|
|
### Agent-Update
|
|
|
|
Bei neuer Agent-Version:
|
|
|
|
```bash
|
|
make agent
|
|
scp build/rmm-agent root@OPNSENSE:/tmp/
|
|
ssh root@OPNSENSE '/bin/sh -c "service rmm_agent stop; cp /tmp/rmm-agent /usr/local/rmm/rmm-agent; chmod +x /usr/local/rmm/rmm-agent; service rmm_agent start"'
|
|
```
|
|
|
|
Oder Plugin komplett neu installieren (`install.sh` ist idempotent).
|
|
|
|
### Hinweise
|
|
|
|
- **Agent-ID** wird persistent in `/usr/local/rmm/agent_id` gespeichert — ueberlebt Neuinstallationen
|
|
- **Config** wird bei "Speichern & Anwenden" aus dem OPNsense-Model nach `/usr/local/rmm/config.yaml` generiert
|
|
- **Logs**: `/var/log/rmm-agent.log`
|
|
- **PID-File**: `/var/run/rmm_agent.pid`
|
|
|
|
## 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 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
|
|
|
|
```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 <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)
|
|
|
|
```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)
|
|
│ │ └── 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.
|