Debian 13 (Trixie) liefert PostgreSQL 17.8, TimescaleDB benoetigt >= 17.9. Loesung: offizielles PostgreSQL-Apt-Repo (apt.postgresql.org/bookworm-pgdg) statt Debian-eigene Pakete verwenden. Fehlerbehebung fuer bestehende Installationen ebenfalls dokumentiert.
370 lines
11 KiB
Markdown
370 lines
11 KiB
Markdown
# PostgreSQL + TimescaleDB Setup
|
|
|
|
Einrichtung von PostgreSQL mit TimescaleDB auf dem RMM-Backend-Server (Debian 13 Trixie).
|
|
|
|
> **Hinweis:** Das Backend legt alle Tabellen beim ersten Start **automatisch an** (Auto-Migration inkl. ALTER TABLE für neue Spalten). Das Schema muss **nicht** manuell erstellt werden. Diese Datei dient als Referenz und Installationsanleitung.
|
|
|
|
---
|
|
|
|
## 1. PostgreSQL installieren
|
|
|
|
> **Wichtig für Debian 13 (Trixie):** Das Standard-Debian-Repository liefert PostgreSQL 17.8,
|
|
> TimescaleDB benötigt aber mindestens 17.9. Deshalb muss zuerst das **offizielle
|
|
> PostgreSQL-Apt-Repository** eingerichtet werden — das liefert immer die aktuelle Version.
|
|
|
|
```bash
|
|
apt update
|
|
apt install -y curl gnupg
|
|
|
|
# Offizielles PostgreSQL-Repository einrichten
|
|
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
|
|
| gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
|
|
|
|
echo "deb https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" \
|
|
> /etc/apt/sources.list.d/pgdg.list
|
|
|
|
apt update
|
|
apt install -y postgresql-17
|
|
|
|
systemctl enable postgresql
|
|
systemctl start postgresql
|
|
|
|
# Version prüfen — muss 17.9 oder neuer zeigen
|
|
psql --version
|
|
```
|
|
|
|
---
|
|
|
|
## 2. TimescaleDB installieren
|
|
|
|
```bash
|
|
# TimescaleDB-Repository einrichten
|
|
echo "deb https://packagecloud.io/timescale/timescaledb/debian/ bookworm main" \
|
|
> /etc/apt/sources.list.d/timescaledb.list
|
|
curl -sL https://packagecloud.io/timescale/timescaledb/gpgkey \
|
|
| gpg --dearmor > /etc/apt/trusted.gpg.d/timescaledb.gpg
|
|
apt update
|
|
|
|
# TimescaleDB für PostgreSQL 17 installieren
|
|
apt install -y timescaledb-2-postgresql-17
|
|
|
|
# In postgresql.conf aktivieren
|
|
sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = 'timescaledb'/" \
|
|
/etc/postgresql/17/main/postgresql.conf
|
|
|
|
systemctl restart postgresql
|
|
```
|
|
|
|
> **Falls der Install mit einem Abhängigkeitsfehler fehlschlägt** (`postgresql-17 >= 17.9 required`):
|
|
> Das bedeutet, dass noch das Debian-eigene PostgreSQL 17.8 installiert ist.
|
|
> Lösung:
|
|
> ```bash
|
|
> apt remove -y postgresql* && apt autoremove -y
|
|
> # Dann Schritt 1 (PostgreSQL aus dem pgdg-Repo) wiederholen
|
|
> ```
|
|
|
|
---
|
|
|
|
## 3. Datenbank und User anlegen
|
|
|
|
```bash
|
|
sudo -u postgres psql << 'EOF'
|
|
CREATE DATABASE rmm;
|
|
CREATE USER rmm WITH PASSWORD 'SICHERES_PASSWORT';
|
|
GRANT ALL PRIVILEGES ON DATABASE rmm TO rmm;
|
|
\c rmm
|
|
GRANT ALL ON SCHEMA public TO rmm;
|
|
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
|
\dx
|
|
EOF
|
|
```
|
|
|
|
---
|
|
|
|
## 4. pg_hba.conf anpassen
|
|
|
|
```bash
|
|
echo "host rmm rmm 127.0.0.1/32 scram-sha-256" >> /etc/postgresql/17/main/pg_hba.conf
|
|
systemctl reload postgresql
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Verbindung testen
|
|
|
|
```bash
|
|
psql -h 127.0.0.1 -U rmm -d rmm -c "SELECT extname, extversion FROM pg_extension WHERE extname = 'timescaledb';"
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Schema (Referenz — wird automatisch angelegt)
|
|
|
|
### `agents` — Agent-Registry
|
|
|
|
```sql
|
|
CREATE TABLE agents (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
hostname TEXT NOT NULL,
|
|
ip TEXT NOT NULL,
|
|
opnsense_version TEXT NOT NULL DEFAULT '',
|
|
agent_version TEXT NOT NULL DEFAULT '',
|
|
platform TEXT NOT NULL DEFAULT 'freebsd',
|
|
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_heartbeat TIMESTAMPTZ,
|
|
customer_id INTEGER REFERENCES customers(id),
|
|
update_requested BOOLEAN NOT NULL DEFAULT FALSE
|
|
);
|
|
CREATE INDEX idx_agents_customer ON agents(customer_id);
|
|
```
|
|
|
|
### `customers` — Kundenstammdaten
|
|
|
|
```sql
|
|
CREATE TABLE customers (
|
|
id SERIAL PRIMARY KEY,
|
|
number TEXT NOT NULL UNIQUE, -- z.B. "K05068"
|
|
name TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### `users` — Frontend-Benutzer
|
|
|
|
```sql
|
|
CREATE TABLE users (
|
|
id SERIAL PRIMARY KEY,
|
|
username TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL, -- bcrypt
|
|
display_name TEXT NOT NULL DEFAULT '',
|
|
role TEXT NOT NULL DEFAULT 'admin',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_login TIMESTAMPTZ
|
|
);
|
|
-- Standard-Admin: admin / Start!123 (beim ersten Backend-Start angelegt)
|
|
```
|
|
|
|
### `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,
|
|
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 (Deduplizierung)
|
|
size INTEGER NOT NULL,
|
|
config_xml TEXT NOT NULL,
|
|
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);
|
|
```
|
|
|
|
### `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,
|
|
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
|
|
|
|
```sql
|
|
CREATE TABLE agent_firmware (
|
|
version TEXT NOT NULL,
|
|
platform TEXT NOT NULL DEFAULT 'freebsd', -- "freebsd","linux","windows"
|
|
hash TEXT NOT NULL, -- SHA256
|
|
binary_data BYTEA NOT NULL,
|
|
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (version, platform)
|
|
);
|
|
```
|
|
|
|
### `tasks` — Task-Scheduler
|
|
|
|
```sql
|
|
CREATE TABLE tasks (
|
|
id SERIAL PRIMARY KEY,
|
|
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
name TEXT DEFAULT '',
|
|
action TEXT NOT NULL, -- "update","backup","reboot",...
|
|
status TEXT NOT NULL DEFAULT 'scheduled',
|
|
schedule_type TEXT DEFAULT 'once', -- "once","daily","weekly","monthly"
|
|
schedule_time TEXT DEFAULT '02:00',
|
|
schedule_weekday INT, -- 0=So..6=Sa
|
|
schedule_monthday INT, -- 1-31
|
|
reboot BOOLEAN DEFAULT FALSE,
|
|
health_check BOOLEAN DEFAULT FALSE,
|
|
health_check_timeout INT DEFAULT 600,
|
|
active BOOLEAN DEFAULT TRUE,
|
|
scheduled_at TIMESTAMPTZ NOT NULL,
|
|
started_at TIMESTAMPTZ,
|
|
finished_at TIMESTAMPTZ,
|
|
last_run TIMESTAMPTZ,
|
|
next_run TIMESTAMPTZ,
|
|
run_count INT DEFAULT 0,
|
|
result JSONB,
|
|
webhook_url TEXT,
|
|
webhook_sent BOOLEAN DEFAULT FALSE,
|
|
webhook_response TEXT,
|
|
created_by TEXT,
|
|
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;
|
|
```
|
|
|
|
### `api_keys` — API-Keys
|
|
|
|
```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,
|
|
filename TEXT NOT NULL,
|
|
data BYTEA NOT NULL,
|
|
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,
|
|
target_type TEXT,
|
|
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
|
|
|
|
```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
|
|
|
|
```sql
|
|
CREATE TABLE metrics (
|
|
time TIMESTAMPTZ NOT NULL,
|
|
agent_id TEXT NOT NULL,
|
|
metric TEXT NOT NULL,
|
|
value DOUBLE PRECISION NOT NULL,
|
|
tags JSONB
|
|
);
|
|
SELECT create_hypertable('metrics', 'time', if_not_exists => TRUE);
|
|
CREATE INDEX idx_metrics_agent_metric ON metrics(agent_id, metric, time DESC);
|
|
|
|
-- Retention: Rohdaten nach 90 Tagen löschen
|
|
SELECT add_retention_policy('metrics', INTERVAL '90 days');
|
|
|
|
-- Compression: Daten älter als 7 Tage komprimieren
|
|
ALTER TABLE metrics SET (
|
|
timescaledb.compress,
|
|
timescaledb.compress_segmentby = 'agent_id, metric',
|
|
timescaledb.compress_orderby = 'time DESC'
|
|
);
|
|
SELECT add_compression_policy('metrics', INTERVAL '7 days');
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Nützliche Abfragen
|
|
|
|
```sql
|
|
-- Alle Agents mit Status
|
|
SELECT id, name, hostname, ip, opnsense_version, agent_version,
|
|
last_heartbeat,
|
|
CASE
|
|
WHEN last_heartbeat > NOW() - INTERVAL '2 min' THEN 'online'
|
|
WHEN last_heartbeat > NOW() - INTERVAL '5 min' THEN 'stale'
|
|
ELSE 'offline'
|
|
END AS status
|
|
FROM agents ORDER BY name;
|
|
|
|
-- CPU-Auslastung letzte 24h (5-Minuten-Durchschnitt)
|
|
SELECT time_bucket('5 minutes', time) AS t, agent_id, AVG(value) AS cpu_avg
|
|
FROM metrics
|
|
WHERE metric = 'cpu_usage' AND time > NOW() - INTERVAL '24 hours'
|
|
GROUP BY t, agent_id ORDER BY t;
|
|
|
|
-- Disk-Warnung: > 80% belegt
|
|
SELECT agent_id, tags->>'mountpoint' AS mount, value AS used_pct, time
|
|
FROM metrics
|
|
WHERE metric = 'disk_used_percent' AND value > 80
|
|
AND time > NOW() - INTERVAL '1 hour'
|
|
ORDER BY value DESC;
|
|
|
|
-- Offene Tasks
|
|
SELECT t.id, a.name AS agent, t.action, t.status, t.scheduled_at, t.webhook_url
|
|
FROM tasks t JOIN agents a ON t.agent_id = a.id
|
|
WHERE t.status IN ('scheduled','running')
|
|
ORDER BY t.scheduled_at;
|
|
|
|
-- Datenbankgrösse
|
|
SELECT pg_size_pretty(pg_database_size('rmm')) AS db_size;
|
|
SELECT relname AS tabelle, pg_size_pretty(pg_total_relation_size(relid)) AS groesse
|
|
FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC;
|
|
```
|
|
|
|
---
|
|
|
|
## Übersicht
|
|
|
|
| Tabelle | Typ | Beschreibung |
|
|
|---------|-----|-------------|
|
|
| `agents` | Relational | Agent-Registry |
|
|
| `customers` | Relational | Kundenstammdaten |
|
|
| `users` | Relational | Frontend-Benutzer |
|
|
| `system_data` | Relational | Aktueller Systemzustand (JSONB) |
|
|
| `config_backups` | Relational | Versionierte OPNsense-Configs |
|
|
| `agent_events` | Relational | Online/Offline-Ereignisse |
|
|
| `agent_firmware` | Relational | Agent-Binaries pro Plattform |
|
|
| `tasks` | Relational | Task-Scheduler mit Webhook |
|
|
| `api_keys` | Relational | API-Keys |
|
|
| `installers` | Relational | OPNsense Plugin-Pakete |
|
|
| `audit_log` | Relational | Audit-Protokoll |
|
|
| `system_settings` | Relational | Backend-Einstellungen (Key/Value) |
|
|
| `metrics` | TimescaleDB | Zeitreihen (CPU, RAM, Disk, Network, Gateway) |
|