Zum Inhalt

Architektur

Übersicht

Vesana ist ein push-basiertes Monitoring-System: Agents und Collectors verbinden sich von außen zum Server und schicken Check-Ergebnisse rein. Der Server hat keine ausgehende Verbindung zu den überwachten Maschinen.

Drei Monitoring-Modi

Jeder Check läuft in genau einem von drei Modi (check_mode). Alle drei führen denselben Check-Code aus — ein SNMP- oder HTTP-Check liefert das gleiche Ergebnis, egal wer ihn ausführt. Nur der Ausführer unterscheidet sich:

Modus Ausführer Typischer Einsatz
passive Collector (Linux-VM im Kundennetz) SNMP, Ping, SSH, HTTP gegen Geräte ohne eigenen Agent
agent Agent (Go-Binary direkt auf der Zielmaschine) CPU/RAM/Disk/Services auf Windows- und Linux-Servern
active Aktiver Collector (Server prüft selbst) Kein Collector im Kundennetz vorhanden oder gewünscht

Mix pro Host ist normal, nicht die Ausnahme. Ein Host kann gleichzeitig Agent-Checks (lokale CPU-Last) und passive SNMP-Checks über einen Collector haben, oder SNMP-Checks, die mangels Collector auf active laufen. check_mode ist eine Eigenschaft des einzelnen Checks (host_services.check_mode), nicht des Hosts — deshalb lässt sich pro Check frei wählen, wer ihn ausführt, solange die Capabilities passen (siehe Profile & Checks → Capabilities filtern Check-Typen).

Fehlt für einen als „passive" angelegten Check ein Collector, fällt er automatisch auf active zurück, statt unausführbar zu bleiben — kein Check geht dadurch verloren.

flowchart LR
    subgraph "Im Kundennetz"
        A[Agent auf Servern] -->|HTTPS POST| EXIT[outbound 443]
        C[Collector VM] -->|HTTPS POST| EXIT
    end

    subgraph "Vesana-Server"
        EXIT --> R[Receiver]
        R --> RS[(Redis Stream)]
        RS --> W1[Worker 0]
        RS --> W2[Worker 1]
        RS --> W3[Worker N]
        W1 --> DB[(Postgres + TimescaleDB)]
        W2 --> DB
        W3 --> DB
        DB --> API[REST API]
        API --> FE[React Frontend]
        API --> M[Installierte Web-App / Browser]
    end

Komponenten

Receiver

  • FastAPI-Service, der Agent- und Collector-Pakete entgegennimmt
  • Authentifiziert per X-API-Key (Collector) oder X-Agent-Token (Agent)
  • Validiert das Schema, schreibt sofort in den Redis Stream — keine Check-Auswertung
  • Ziel: möglichst kurze Latenz, möglichst hohe Durchsatzrate

Redis Stream

  • Backpressure-fähige Eingangs-Queue (XADD / XREADGROUP)
  • Mehrere Worker konsumieren parallel
  • Im Normalbetrieb hält der Worker den Stream kurz. Stehen die Worker länger still (Absturz, Datenbank weg), ist der Stream bei rund 100.000 Einträgen gedeckelt — darüber fallen die ältesten Einträge weg, damit Redis nicht vollläuft. Redis selbst läuft mit noeviction: ist der Speicher voll, schlagen Schreibzugriffe fehl, statt still Daten zu verdrängen

Worker

  • Liest Messages, holt Host-/Service-Kontext aus der DB
  • Wendet Profile-Check-Effective-Config an, normalisiert Werte
  • Schreibt Check-Ergebnisse in check_results (Hypertable)
  • Aktualisiert current_status (Hot-Table mit fillfactor=80)
  • Triggert Alert-Auswertung, Notification-Versand, AI-Analyse-Cache-Invalidierung

API

  • FastAPI mit JWT-Auth, Tenant-Scope automatisch über ORM-Filter
  • Endpoints: Hosts, Services, Profile, Discoveries, Alerts, Reports, Wiki, AI, Admin
  • Background-Tasks: Downtime-Watcher, Dead-Collector-Watcher, Anomalien & Prognosen (Normalbereiche, Bewertung, Prognosen), Auto-Purge, Instanz-Puls, Lizenz-Abholung
  • Distributed Locking via Redis — bei mehreren API-Replicas läuft jeder Watcher nur einmal

Frontend

  • React 18 + TypeScript + Vite
  • Themed via CSS-Variablen (13 Themes plus ein eigenes Theme, je hell/dunkel)
  • Lazy-Loaded ECharts für Charts, Lazy-Loaded ReactMarkdown für Wiki

Agent (Go)

  • Single-Binary, statisch gelinkt (CGO_ENABLED=0), ~6.5 MB
  • Holt Config alle 5 Minuten, führt Checks lokal aus
  • Auto-Update beim Config-Refresh wenn Server eine neuere Version meldet

Collector (Go)

  • Single-Binary, läuft im Kundennetz auf einer Linux-VM
  • Führt Remote-Checks aus: SNMP, Ping, SSH, HTTP, Discovery (eigener Netzwerk-Scanner)
  • Holt Config alle 60 Sekunden, schickt Ergebnisse + Discovery-Resultate an Server

Aktiver Collector (Go)

  • Dasselbe Collector-Binary, aber als systemd-Service (vesana-active-collector) direkt auf der Vesana-Maschine
  • Führt check_mode='active'-Checks tenant-übergreifend aus — server-global, max. eine Instanz pro Vesana-Installation
  • Sinnvoll, wenn kein Collector im Kundennetz betrieben werden soll oder ein passiver Check mangels Collector sonst gar nicht liefe
  • Ist der Aktive Collector offline, springt ein Python-Hybrid-Fallback im Worker ein — Checks bleiben lauffähig, nur langsamer

Smartphone (installierbare Web-App)

  • Keine native App mehr: Vesana wird als Web-App auf den Startbildschirm gelegt (Android, iPhone, iPad)
  • Push nach dem Browser-Standard Web Push (VAPID) — jede Instanz erzeugt ihr Schlüsselpaar selbst, kein Google-/Apple-Konto nötig
  • Tipp auf eine Push-Meldung → Host-Detail-Seite mit geöffnetem Check

Multi-Tenant-Isolation

Tenants sind die zentrale Trennlinie. Jede DB-Tabelle mit Kunden-Daten hat eine tenant_id-Spalte; die API filtert jede Abfrage auf die Tenants, die der Aufrufer sehen darf.

Super-Admins sehen alles. Alle anderen Konten erhalten Rechte über Rollen-Zuweisungen: Rolle × Bereich, wobei der Bereich ein Tenant, eine Tenant-Gruppe oder „alle Tenants" ist. Zuweisungen gehen an Benutzer oder an Teams. Ein Konto kann so in verschiedenen Tenants verschiedene Rollen haben — z. B. Admin bei Kunde A, nur lesen bei Kunde B. Details: Rollen.

flowchart LR
    U[Benutzer] --> Z1[Zuweisung: Admin @ Tenant A]
    U --> T[Team]
    T --> Z2[Zuweisung: Viewer @ Tenant-Gruppe Kunden]
    Z1 --> A[(Tenant A)]
    Z2 --> B[(Tenant B)]
    Z2 --> C[(Tenant C)]

Sicherheits-Architektur

Acht Säulen:

1 Verschlüsselung sensibler Felder

shared/encryption.py bietet encrypt_field() / decrypt_field() (AES-256-GCM). Verschlüsselt werden u. a. SNMP-Communities, SSH-Passwörter. Schlüssel: FIELD_ENCRYPTION_KEY (Base64url, 32 Bytes). Der Server hält Plaintext nur kurz im RAM.

Details: Sicherheit → Verschlüsselung.

2 Token-basierte Authentifizierung

Token Format Speicherung Wer benutzt
Sitzungs-Token (JWT) HS256 HttpOnly-Cookie im Browser Endbenutzer-Login
Agent-Token vesana_agent_ + 32 Zufallsbytes (URL-sicher) SHA256-Hash in agent_tokens.token_hash Agent zum Receiver
API-Key vesana_ + 32 Zufallsbytes (URL-sicher) SHA256-Hash in api_keys.key_hash Collector zum Receiver

Plaintext wird nie in der DB gespeichert — nur Hashes. Keys sind genau einmal sichtbar (beim Erzeugen).

3 Zwei-Faktor-Authentifizierung

Pro User wählbar: TOTP (Authenticator-App) oder WebAuthn (Hardware-Key/Passkey). E-Mail-Codes gibt es nicht mehr. Details: Sicherheit → Härtungs-Checkliste.

4 Rate-Limiting

Anmeldung, 2FA-Prüfung und Identitätsbestätigung sind per IP auf 10 Anfragen/min gedeckelt, Passwort-Änderung auf 5/min. Zweck: Brute-Force gegen schwache Passwörter und 2FA-Codes verlangsamen.

5 RBAC — deny-by-default

Jeder API-Endpunkt verlangt eine explizite Permission (hosts.create, alert_rules.edit, …) — ohne passende Permission gibt es 403, nicht implizites Erlauben. Vier mitgelieferte Rollen (Super Admin, Admin, Operator, Viewer) plus eigene Rollen mit granularer Permission-Auswahl. Welche Rolle in welchem Tenant gilt, bestimmen die Rollen-Zuweisungen (siehe Multi-Tenant-Isolation).

6 Script-Ausführung — Apply-Scope + Sandbox

Script-fähige Check-Typen (agent_script, agent_custom, plugin_local, ssh_script, ssh_custom, ssh_command, api_script) können beliebigen Code ausführen — deshalb greift ein eigener Autorisierungs-Layer: der Apply-Scope pro User (nach Tenant, Tag oder einzelnem Host), deny-by-default, Super-Admin ausgenommen. Wer Scripts anlegt oder deren Inhalt ändert, bestätigt zusätzlich seine Identität — auch Super-Admins. Der Check-Executor selbst läuft in einer Sandbox mit eingeschränkten Capabilities.

Details: Sicherheit → Härtungs-Checkliste.

7 Container-Isolation

API-, Worker-, Receiver- und AI-Service-Container laufen als non-root. Einzige Ausnahme: der Updater-Container bleibt root, weil er den Docker-Socket beschreiben muss, um Updates auszuführen.

8 Distributed Locking

Mehrere API-Replicas? downtime_expiry_watcher, dead_collector_watcher etc. laufen nur einmal — Redis-Locks mit 55 s Timeout sorgen dafür.

Sub-Systeme

Profile + Checks

Zweistufiges Modell. Hosts haben ein primäres Profil (z. B. „APC Smart-UPS"), optional weitere. Profile haben Profile-Checks (z. B. „Battery Voltage"). host_services sind Instanzen pro Host mit optionalen Overrides.

Details: Profile & Checks.

Aggregate (K-von-N)

Ein Aggregat ist ein eigenständiges Objekt (kein normaler Check), das den Status einer homogenen Gruppe redundanter Mitglieder (z. B. 2 Uplinks, 3 Netzteile) read-time gegen eine Schwelle K auswertet: gesund == N → OK, K ≤ gesund < N → WARNING („Redundanz angeknabbert, läuft noch"), gesund < K → CRITICAL („Funktion verloren"). Die WARNING-Stufe unterdrückt Downstream-Abhängigkeiten bewusst nicht — erst bei echtem CRITICAL greift die normale Inhibition.

Wiki + AI

Eigene Wissensbasis (Markdown, FTS, pgvector). AI greift via RAG ins Wiki, fällt auf Web-Suche zurück, kennzeichnet Quellen.

Auto-Discovery

Collector scannt das Netzwerk mit seinem eigenen Scanner (ARP, ICMP, TCP, Dienst-Erkennung). SNMP-sysOID matcht auf Profile. Bei Auto-Match wird das Profil als Vorschlag übernommen.

Performance-Modell

Auf Self-Hosting-Defaults (1 Worker, 256 MB shared_buffers): ~960 checks/s sustained, p95 ≤ 150 ms, 0 Errors über 30-Min-Soak. Skalierungs-Hebel: Administration → Skalierung.

Anschluss