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 Logik, kein DB-Zugriff
  • Ziel: möglichst kurze Latenz, möglichst hohe Durchsatzrate

Redis Stream

  • Backpressure-fähige Eingangs-Queue (XADD / XREADGROUP)
  • Mehrere Worker konsumieren parallel
  • Bei vollem Stream lehnt der Receiver ab (noeviction-Policy) — kein stilles Drop

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, Anomaly-Baselines, Auto-Purge, Tester-Phone-Home
  • Distributed Locking via Redis — bei mehreren API-Replicas läuft jeder Watcher nur einmal

Frontend

  • React 18 + TypeScript + Vite
  • Themed via CSS-Variablen (20 Themes × dark/light)
  • 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 (nmap)
  • 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. Auf ORM-Ebene erzwingt apply_tenant_filter() (api/app/auth.py) die Filterung — wer eine Query ohne Tenant-Scope absetzt, kriegt einen Runtime-Error.

Super-Admins haben Tenant-Scope null und sehen alles. Normale Nutzer sind an einen Tenant gebunden, mit optionalem Cross-Tenant-Read in Custom-Roles.

flowchart TB
    subgraph Super-Admin
        SA[user.tenant_scope = null] --> ALLES[(alle Tenants)]
    end
    subgraph Tenant A
        UA[user.tenant_id = A] --> A[(Hosts/Alerts A)]
    end
    subgraph Tenant B
        UB[user.tenant_id = B] --> B[(Hosts/Alerts B)]
    end

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
User-JWT RS256 Browser/Mobile lokal Endbenutzer-Login
Agent-Token vesana_agent_ + 32 url-safe Base64 SHA256-Hash in agent_tokens.token_hash Agent zum Receiver
API-Key Custom-Prefix + 32 Bytes 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

Login-Endpoint und 2FA-Verify sind per IP auf 10 req/min gedeckelt, 2FA-Resend auf 3 req/min (slowapi). Zweck: Brute-Force gegen schwache Passwörter und 2FA-Codes verlangsamen.

5 RBAC — deny-by-default

Jeder API-Endpunkt verlangt eine explizite Permission (host.create, alert_rule.edit, …) — ohne passende Permission gibt es 403, nicht implizites Erlauben. Vier Basis-Rollen (Super-Admin, Admin, Operator, Viewer) plus Custom-Roles mit granularer Permission-Auswahl. tenant_access steuert, ob ein User nur seinen eigenen Tenant sieht oder mehrere/alle.

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

Script-fähige Check-Typen (agent_script, custom, ssh_script, ssh_custom) 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. 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 Profil (z. B. „APC Smart-UPS"). 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.

Policies

Deklaratives Regelsystem: Match-Bedingungen (JsonLogic-Subset) + Actions, die automatisch Tags setzen oder Checks anlegen — Bulk-Konfiguration ohne SSH-Schlepperei. Ein Form-Builder und ein AI-Generator helfen beim Formulieren der Bedingungen.

Umgesetzte Action-Typen: add_check (Check automatisch anlegen), tag_assignment (Tag automatisch setzen), config_patch (Config-Override patchen). Weitere Action-Typen sind im Schema vorgesehen, aber noch nicht umgesetzt — das UI zeigt nur implementierte Typen an, das Backend lehnt Policies ab, die ausschließlich nicht-umgesetzte Actions enthalten.

Sicherheitsnetz: Dry-Run ist vor dem Speichern Pflicht, ein Circuit-Breaker stoppt den Sync bei ungewöhnlich vielen Löschungen oder Operationen, neue Policies wenden sich standardmäßig nicht rückwirkend auf Bestandshosts an (Adoption muss explizit gewählt werden). Policies, die Scripts ausrollen würden, sind unabhängig von der sonstigen Policy-Berechtigung Super-Admin-only (siehe Säule 6 oben).

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 Netzwerk per nmap. SNMP-sysOID matcht auf Profile. Bei Auto-Match wird das Profil als Vorschlag übernommen.

NSCA-Empfänger

Optionaler Receiver auf Port 5667. Nimmt Pakete von send_nsca-Clients an. Migrations-Pfad für Nagios-Bestände.

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