docs: BACKEND.md vollständiges DB-Schema mit allen Feldern + jwt_secret in config

This commit is contained in:
cynfo3000 2026-03-06 09:26:35 +01:00
parent 48ac353d28
commit 9bebf85a6d

View File

@ -82,7 +82,8 @@ listen_addr: ":8443"
tls_cert: "certs/server.crt" tls_cert: "certs/server.crt"
tls_key: "certs/server.key" tls_key: "certs/server.key"
api_keys: api_keys:
- "SICHERER_API_KEY" - "SICHERER_API_KEY" # beliebig lang, wird in X-API-Key Header mitgegeben
jwt_secret: "LANGER_ZUFAELLIGER_STRING" # fuer Frontend-Login (JWT-Signierung)
database: database:
host: "127.0.0.1" host: "127.0.0.1"
@ -93,6 +94,12 @@ database:
sslmode: "disable" # "require" wenn PG ueber Netz sslmode: "disable" # "require" wenn PG ueber Netz
``` ```
Zufälligen JWT-Secret generieren:
```bash
openssl rand -hex 32
```
Alternativ per Environment-Variablen: `RMM_LISTEN_ADDR`, `RMM_TLS_CERT`, `RMM_TLS_KEY`, `RMM_DB_HOST`, `RMM_DB_PASSWORD`. Alternativ per Environment-Variablen: `RMM_LISTEN_ADDR`, `RMM_TLS_CERT`, `RMM_TLS_KEY`, `RMM_DB_HOST`, `RMM_DB_PASSWORD`.
## 6. Backend deployen und starten ## 6. Backend deployen und starten
@ -141,27 +148,285 @@ Beim ersten Start:
- Retention Policy (90 Tage) und Compression Policy (nach 7 Tagen) werden automatisch gesetzt - Retention Policy (90 Tage) und Compression Policy (nach 7 Tagen) werden automatisch gesetzt
- Default-Admin `admin` / `YOUR_PASSWORD` wird erstellt - Default-Admin `admin` / `YOUR_PASSWORD` wird erstellt
## Datenbank-Schema (automatisch) ## Datenbank-Schema (vollständig)
| Tabelle | Typ | Beschreibung | Alle Tabellen werden beim Backend-Start **automatisch angelegt** (Auto-Migration inkl. ALTER TABLE für neue Spalten). Kein manuelles Schema-Setup nötig.
|---------|-----|-------------|
| `agents` | Relational | Agent-Registry (ID, Name, Hostname, IP, Version, Heartbeat, customer_id, update_requested) |
| `system_data` | Relational | Letzte Systemdaten pro Agent (JSONB) |
| `config_backups` | Relational | OPNsense Config-Backups (dedupliziert per SHA256) |
| `agent_events` | Relational | Online/Offline/Connected Events |
| `customers` | Relational | Kundenstammdaten (Nummer, Name) |
| `users` | Relational | Benutzer (Username, bcrypt-Hash, Display-Name) |
| `agent_firmware` | Relational | Agent-Binaries pro Plattform+Version (BYTEA, SHA256) |
| `metrics` | TimescaleDB Hypertable | Time-Series Metriken (CPU, RAM, Disk, Network, Gateway) |
### TimescaleDB Policies ---
- **Retention**: Rohdaten werden nach 90 Tagen automatisch geloescht ### `agents` — Agent-Registry
- **Compression**: Daten aelter als 7 Tage werden komprimiert (segmentby: agent_id+metric)
```sql
CREATE TABLE agents (
id TEXT PRIMARY KEY, -- UUID des Agents (persistent)
name TEXT NOT NULL, -- Anzeigename (z.B. "OPN.intra.kunde.de")
hostname TEXT NOT NULL, -- Hostname der Firewall
ip TEXT NOT NULL, -- IP-Adresse des Agents
opnsense_version TEXT NOT NULL DEFAULT '', -- OPNsense-Version (z.B. "25.1.1")
agent_version TEXT NOT NULL DEFAULT '', -- Agent-Binary-Version (z.B. "1.0.3")
platform TEXT NOT NULL DEFAULT 'freebsd', -- Plattform: "freebsd", "linux"
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Erstregistrierung
last_heartbeat TIMESTAMPTZ, -- Letzter Heartbeat
customer_id INTEGER REFERENCES customers(id), -- Kundenzuordnung (nullable)
update_requested BOOLEAN NOT NULL DEFAULT FALSE -- Update-Flag fuer Updater-Daemon
);
CREATE INDEX idx_agents_customer ON agents(customer_id);
```
**Status-Berechnung** (kein DB-Feld, dynamisch im Backend):
| Status | Bedingung |
|--------|-----------|
| `online` | `last_heartbeat` < 2 Minuten |
| `stale` | `last_heartbeat` < 5 Minuten |
| `offline` | `last_heartbeat` > 5 Minuten |
| `unknown` | `last_heartbeat` ist NULL |
---
### `customers` — Kundenstammdaten
```sql
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
number TEXT NOT NULL UNIQUE, -- RMM-Kundennummer (z.B. "K05068")
name TEXT NOT NULL, -- Kundenname
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
### `users` — Benutzer (Frontend-Login)
```sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL, -- bcrypt-Hash
display_name TEXT NOT NULL DEFAULT '',
role TEXT NOT NULL DEFAULT 'admin', -- aktuell nur "admin"
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_login TIMESTAMPTZ -- nullable
);
```
Standard-Admin wird beim ersten Start angelegt: `admin` / `Start!123` (sofort ändern).
---
### `system_data` — Aktueller Systemzustand pro Agent
```sql
CREATE TABLE system_data (
agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
data_json JSONB NOT NULL, -- Vollständiger Heartbeat-Payload des Agents
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
### `config_backups` — Versionierte OPNsense-Configs
```sql
CREATE TABLE config_backups (
id SERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
hash TEXT NOT NULL, -- SHA256 der config.xml (Deduplizierung)
size INTEGER NOT NULL, -- Grösse in Bytes
config_xml TEXT NOT NULL, -- Vollständige config.xml
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_config_backups_agent ON config_backups(agent_id, created_at DESC);
CREATE INDEX idx_config_backups_hash ON config_backups(agent_id, hash);
```
Deduplizierung: gleicher SHA256 → kein neuer Eintrag.
---
### `agent_events` — Online/Offline-Ereignisse
```sql
CREATE TABLE agent_events (
id SERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
event_type TEXT NOT NULL, -- "online", "offline", "stale", "connected", "disconnected"
message TEXT, -- optionale Zusatzinfo
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_agent_events_agent ON agent_events(agent_id, created_at DESC);
```
---
### `agent_firmware` — Agent-Binaries pro Plattform
```sql
CREATE TABLE agent_firmware (
version TEXT NOT NULL, -- z.B. "1.0.3"
platform TEXT NOT NULL DEFAULT 'freebsd', -- "freebsd", "linux", "windows"
hash TEXT NOT NULL, -- SHA256 des Binaries
binary_data BYTEA NOT NULL, -- Das Binary selbst
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (version, platform)
);
```
---
### `tasks` — Task-Scheduler (geplante Aufgaben)
```sql
CREATE TABLE tasks (
id SERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
name TEXT DEFAULT '', -- Anzeigename der Aufgabe
action TEXT NOT NULL, -- "update", "backup", "reboot", ...
status TEXT NOT NULL DEFAULT 'scheduled', -- "scheduled","running","completed","failed"
schedule_type TEXT DEFAULT 'once', -- "once", "daily", "weekly", "monthly"
schedule_time TEXT DEFAULT '02:00', -- Uhrzeit (HH:MM)
schedule_weekday INT, -- 0=So, 1=Mo, ... 6=Sa (nur bei "weekly")
schedule_monthday INT, -- 1-31 (nur bei "monthly")
reboot BOOLEAN DEFAULT FALSE, -- Nach Update neu starten
health_check BOOLEAN DEFAULT FALSE, -- Nach Aufgabe Erreichbarkeit prüfen
health_check_timeout INT DEFAULT 600, -- Sekunden bis Timeout des Health-Checks
active BOOLEAN DEFAULT TRUE, -- Recurring: aktiv/inaktiv
scheduled_at TIMESTAMPTZ NOT NULL, -- Nächste/erste Ausführung (once)
started_at TIMESTAMPTZ, -- Tatsächlicher Start
finished_at TIMESTAMPTZ, -- Ende der Ausführung
last_run TIMESTAMPTZ, -- Letzter Lauf (recurring)
next_run TIMESTAMPTZ, -- Nächster Lauf (recurring)
run_count INT DEFAULT 0, -- Anzahl Ausführungen (recurring)
result JSONB, -- Ergebnis-Payload
webhook_url TEXT, -- URL für Webhook nach Abschluss
webhook_sent BOOLEAN DEFAULT FALSE,
webhook_response TEXT, -- HTTP-Response des Webhooks
created_by TEXT, -- Username des Erstellers
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_tasks_status_scheduled ON tasks(status, scheduled_at);
CREATE INDEX idx_tasks_active_next_run ON tasks(active, next_run) WHERE active = TRUE;
```
**Webhook-Payload** (gesendetes JSON bei `webhook_url` gesetzt):
```json
{
"customer_id": "K05068",
"hostname": "opnsense.kunde.de",
"opnsense_version": "25.1.1",
"timestamp": "2026-03-06T08:00:00Z",
"trigger": "update_completed",
"reachable": true,
"update_status": "completed"
}
```
---
### `api_keys` — API-Keys (zusätzlich zu config.yaml)
```sql
CREATE TABLE api_keys (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
key TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_used TIMESTAMPTZ
);
```
---
### `installers` — OPNsense Plugin-Pakete
```sql
CREATE TABLE installers (
id SERIAL PRIMARY KEY,
platform TEXT NOT NULL UNIQUE, -- "freebsd", "linux"
filename TEXT NOT NULL,
data BYTEA NOT NULL, -- ZIP-Paket (install.sh + Plugin)
sha256 TEXT NOT NULL,
size INTEGER NOT NULL,
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
### `audit_log` — Audit-Protokoll
```sql
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
user_name TEXT NOT NULL,
action TEXT NOT NULL, -- z.B. "login", "delete_agent", "update_customer"
target_type TEXT, -- z.B. "agent", "customer", "user"
target_id TEXT,
target_name TEXT,
details TEXT,
source_ip TEXT
);
CREATE INDEX idx_audit_log_timestamp ON audit_log(timestamp DESC);
```
---
### `system_settings` — Backend-Einstellungen (Key/Value)
```sql
CREATE TABLE system_settings (
key TEXT PRIMARY KEY, -- "backend_url_internal", "backend_url_external", "api_key"
value TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
---
### `metrics` — TimescaleDB Hypertable (Zeitreihen)
```sql
CREATE TABLE metrics (
time TIMESTAMPTZ NOT NULL,
agent_id TEXT NOT NULL,
metric TEXT NOT NULL, -- Name der Metrik
value DOUBLE PRECISION NOT NULL,
tags JSONB -- z.B. {"interface":"igb0"}
);
-- In Hypertable umwandeln:
SELECT create_hypertable('metrics', 'time', if_not_exists => TRUE);
CREATE INDEX idx_metrics_agent_metric ON metrics(agent_id, metric, time DESC);
```
**Verfügbare Metriken:**
| Metrik | Einheit | Tags |
|--------|---------|------|
| `cpu_usage` | Prozent | — |
| `memory_used_percent` | Prozent | — |
| `memory_used_bytes` | Bytes | — |
| `memory_total_bytes` | Bytes | — |
| `uptime_seconds` | Sekunden | — |
| `disk_used_percent` | Prozent | `{"mountpoint":"/","dataset":"zroot/ROOT"}` |
| `disk_used_bytes` | Bytes | `{"mountpoint":"/"}` |
| `interface_rx_bytes` | Bytes | `{"interface":"igb0"}` |
| `interface_tx_bytes` | Bytes | `{"interface":"igb0"}` |
| `gateway_rtt_ms` | Millisekunden | `{"gateway":"WAN_GW"}` |
| `gateway_loss_percent` | Prozent | `{"gateway":"WAN_GW"}` |
**TimescaleDB Policies** (automatisch gesetzt):
- **Retention**: Rohdaten nach 90 Tagen gelöscht
- **Compression**: Daten älter als 7 Tage komprimiert (`segmentby: agent_id, metric`)
---
### DB-Funktionen ### DB-Funktionen
- `GetAgentByName`: Sucht Agent nach Name — verwendet fuer Pre-Registration Matching bei Agent-Registrierung - **`GetAgentByName`**: Sucht Agent nach Name — für Pre-Registration Matching (Agent registriert sich, Backend findet vorregistrierten Platzhalter mit `hostname="(ausstehend)"`).
--- ---