16 KiB
16 KiB
checkbot
Checkmk-Bot für Debian-LXC (Proxmox). Überwacht Checkmk per REST-API, lässt Alarme von externem Ollama analysieren, meldet in Telegram und/oder Element (Matrix). Konfiguration komplett über Web-UI.
Vollständiger Plan: /home/sysops/.claude/plans/ja-alle-bernehmen-eager-lark.md
Arbeitsregeln
- Auf diesem Rechner wird nichts installiert (kein venv, kein pip, keine Pakete). Code nur schreiben, Ausführung und Tests passieren im Ziel-LXC.
- Stand: Phase 1 bis 6 als Code, Phase 7 als Dateien geschrieben, nichts ausgeführt oder getestet (siehe README-Statustabelle).
- Code-Kommentare nur bei echtem WHY.
Festgelegte Entscheidungen
- Plattform: Debian-LXC, unprivileged, 1 Core, 1 GB RAM, Python 3, venv im LXC, systemd, Caddy (HTTPS, interne CA). Kein Docker.
- Checkmk 2.5 ist die Zielversion (Live-System). Alle REST-Annahmen (Spaltennamen für Services/Hosts, Downtime-Endpunkt, Antwortformat) wurden nach dem Stand von 2.3 formuliert und müssen im ersten Lauf gegen 2.5 geprüft werden (OpenAPI-Doku der Instanz unter
/<site>/check_mk/api/1.0/ui/, Integrationstests Phase 8). Nur REST-API (Automation-User, Lese-Rolle). Kein Webhook, kein Notification-Script, kein eingehender Alarm-Port. - Poller: 30 s (UI einstellbar), nur Nicht-OK, Zustandsvergleich in SQLite, nur Hard-States, Mindestdauer einstellbar (Default 0 s), Flapping gebündelt.
- Ollama extern
office.sysops.de:11434. Zwei Modell-Einstellungen (Alarmtext, Agent), Startqwen3, Hermes zum Vergleich. - Chat: Adapter-Interface. Telegram (Long-Polling) und Matrix (direkt über die Client-Server-API mit
httpx, bewusst nichtmatrix-nio: kein libolm/E2EE nötig; eigener Homeserver, unverschlüsselter Raum, Bot-User mit Access-Token). Parallel, einzeln abschaltbar, Ziel pro Routing-Regel. - Alarm-Flow: Rohalarm sofort, KI-Analyse per Edit nachgereicht (Telegram editMessage, Matrix m.replace). Max 8 Zeilen DE. Timeout 60 s, 2 Retries, sonst "KI nicht verfügbar". Bündelung pro Host 60 s, max 10 Prompts/min, Recovery ohne KI. Downtime: kein Push, Verlauf "unterdrückt". UI-Stummschaltung mit Ablauf.
- Befehle: gemeinsamer Handler,
/in Telegram,!in Matrix, Allowlist pro Backend. - Agent-Loop nur für
/ask,/explain, Tiefenanalyse. 5 feste Lese-Tools (get_service_status,get_host_services,get_state_history,get_downtimes,get_problems), max 6 Schritte, 90 s, Tool-Output untrusted, alle Aufrufe im Audit-Log. - Bot liest Checkmk standardmäßig nur (Automation-User mit Lese-Rolle). Einzige Schreibaktion: Downtime setzen, siehe nächster Punkt.
- Ruhigstellen per Chat (
/muteund/downtimein Telegram,!muteund!downtimein Matrix):- A,
mute: Bot-lokale Stummschaltung mit Dauer, Checkmk bleibt unberührt. Standard. Alarme bieten Buttons "2 h stumm" (Telegram Inline-Buttons, Matrix:!mute 2hals Antwort auf die Alarmnachricht). - B,
downtime: echte Checkmk-Downtime per REST (POST /domain-types/downtime/collections/service, Endpunkt noch gegen echte Instanz prüfen). Eigener Checkmk-User nur mit Downtime-Recht, Secret getrennt vom Lese-User. In der UI abschaltbar, standardmäßig aus, maximale Dauer einstellbar (z.B. 24 h). - B nur durch Allowlist-User nach Bestätigung (Host, Service, Dauer, Kommentar anzeigen, dann Button bzw.
ja). Agent und Modell haben keine Schreib-Tools und lösen B nie aus. - Jede Aktion im Audit-Log: User, Backend, Host, Service, Dauer.
- A,
- UI: FastAPI, Jinja2, HTMX, deutsch, hell/dunkel. Lokale Nutzer (Argon2, optional TOTP), Rollen admin/viewer,
checkbot create-admin, Setup-Assistent. - Secrets in SQLite mit AES-GCM. Nur
master.key, Session-Secret, Bind-Adresse in/etc/checkbot/(chmod 600). UI zeigt Secrets nie an. - Audit-Log ohne Secret-Werte. Prompts versioniert mit Rollback, Testbereich.
- Retention: Verlauf/KI 90 Tage, Audit 1 Jahr.
/healthplus Checkmk-Agent im LXC. - Plugin-Output ist untrusted Input (Prompt Injection), KI löst keine Aktionen aus.
Phasen
- Gerüst: Schema,
crypto.py, Checkmk- und Ollama-Client,/health - UI-Basis: Login, Rollen, Setup-Assistent, Verbindungstests, Audit
- Chat-Interface, Telegram, Poller, Alarm-Flow
- Matrix-Adapter, Routing, Stummschaltung (UI)
- Command-Handler (inkl.
mute,downtimemit Bestätigung), Agent-Loop - Prompt-Versionierung, Testbereich, Verlauf, Retention
- Deployment (
setup-lxc.sh, Caddy, systemd, Checkmk-Agent) - Tests
Geplante Struktur
app/ main.py fileconfig.py db.py models.py crypto.py checkmk.py poller.py
ollama.py pipeline.py agent.py tools.py commands.py audit.py
retention.py health.py chat/{base,telegram,matrix}.py web/
deploy/ setup-lxc.sh systemd/checkbot.service Caddyfile
tests/
Fortschritt Phase 5b (ungetestet)
- Neu:
app/listeners/(Telegram-getUpdates, Matrix-/sync, Backoff, Offset/Since in Tabellelistener_state),app/downtime/(Client, Bestätigungs-Store, Service),app/agent_runs/(Recorder, Tabelleagent_runs, AgentService),app/commands/{mutecmd,downtimecmd,callbacks,...},app/alerts/extras.py(Buttons/Hinweis),app/chatops.py(Verdrahtung), UI/agentund Sectioncheckmk_dt. - Annahmen Telegram:
getUpdatesmitallowed_updates=[message, callback_query], Long-Poll 30 s; erster Start überspringt den Rückstand peroffset=-1;editMessageTextohnereply_markupwürde die Buttons entfernen, daher wird es beim Edit mitgegeben;callback_datamax. 64 Byte (mute:<alert_id>:<min>,deep:<id>,dt:<id>:<min>,dty:<token>,dtn:<token>). - Annahmen Matrix:
/syncmit Filter (nur Raum, nurm.room.message);next_batchwird vor der Verarbeitung gespeichert (höchstens einmal); Edits (m.replace) undm.noticewerden ignoriert; Reply perm.relates_to.m.in_reply_to. - Annahmen Checkmk:
POST /domain-types/downtime/collections/servicemitdowntime_type=service,host_name,service_descriptions,start_time/end_time(ISO 8601 UTC,Z),comment; Erfolg ist 204. Noch gegen eine echte Instanz prüfen. - Alarm-Buttons nur bei Einzelalarmen (nicht Recovery, nicht gebündelt); Reply-Auflösung bei mehreren Services in einer Nachricht lehnt ab und nennt die explizite Form.
Phase 7 Deployment (Dateien unter deploy/, ungetestet, Details in deploy/README.md)
- Genau ein uvicorn-Worker: Rate-Limiter, TOTP-Einrichtung und Downtime-Bestätigungen liegen im Prozessspeicher, außerdem liefe der Poller doppelt. Bind-Adresse und Port kommen aus
checkbot.env(Unit nutzt${CHECKBOT_BIND_*}), Caddy-Upstream wird daraus gerendert. - Caddy-Modi:
internal(Caddy-CA, Root-Zertifikat muss verteilt werden) oderfiles(Zertifikat der Unternehmens-CA nach/etc/caddy/certs). Zugriffslog ohne Query, Authorization, Cookie. /healthüber Caddy nur von 127.0.0.1 undCHECKBOT_MONITOR_IP(Detailtexte intern), sonst 403./healthliefert 503, solange ein Teilcheck nicht ok ist, auch bei "nicht konfiguriert".- Local Check: "nicht konfiguriert" ergibt WARN, "deaktiviert" (Telegram/Matrix fehlen im JSON) OK, Fehler/Bot nicht erreichbar CRIT.
master.keywird nie mit der DB gesichert. Schlüssel erzeugtsetup-lxc.shals root und übergibt sie ancheckbot(das Konfigurationsverzeichnis ist fürcheckbotnur lesbar).- Unprivilegierte LXCs: Drop-In
checkbot-lxc-relaxed.conf(setup-lxc.sh --lxc-relaxed).
Phase 6 Prompts und Retention (Code geschrieben, ungetestet)
- Migration Index 5 (sechster Eintrag in
app/db.py): Tabellenprompt_versions(UNIQUE name+version),prompt_active,retention_state(Einzelzeile id=1: ts, origin, counts als JSON) und Indexstate_log_ts. Phase 6b/6c hängen ihre Migrationen dahinter an. app/prompts/: Registry (alert,agent) mit Standardtext, Schutzhinweis undcompose; die Standardtexte bleiben inapp/alerts/prompts.pybzw.app/agent/prompts.py(dort nur Agenten-Prompt in Standardtext +DATA_NOTICEgetrennt,SYSTEM_PROMPTbleibt als Zusammensetzung). Version 1 entsteht beim ersten Zugriff aus dem Standard oder dem Altsettingprompt.alert_system(ungültiger Altwert führt zum Standardtext, das Setting wird danach nie wieder gelesen, aber nicht gelöscht). Validierung: NFKC-Normalisierung, Steuer- und Zero-Width-Zeichen weg, nicht leer, max. 4000 Zeichen,<daten>-Begrenzer werden abgelehnt (nicht entfernt, damit nichts neu zusammenwächst).- Verdrahtung über Provider:
AlertAnalyzer(..., prompt_provider),AgentService(..., prompt_provider),Agent(..., system_prompt); Runtime liefertPromptService.effective_text, das nie wirft (Fallback Standardtext). Kein Cache, jeder Aufruf liest frisch (ein indizierter Select). - Audit:
prompt.init(system),prompt.create,prompt.activate(Name,vN, Nutzer), nie Textinhalt oder Notiz. - Testbereich:
PromptTester(Rate-Limit 6/min pro Nutzer, im Speicher). Alarm-Test nutztbuild_messagesundalert_modelausapp/alerts. Agent-Test nutztAgentService.trial_ask: kein Eintrag inagent_runs, Tool-Aufrufe im Audit untertest:<nutzer>. Fehlertexte werden mit den konfigurierten Secrets maskiert. - Retention-Settings (Min 1, Max 3650, Mutes 365):
retention.alerts_days=90,retention.agent_runs_days=90,retention.state_log_days=30,retention.audit_days=365,retention.mutes_days=7. TageslaufRetentionRunnerals Task in der Runtime: 60 s nach Start, danach Prüfung alle 15 min, Lauf einmal pro Kalendertag ab 03:00; ein Start-Lauf nur, wenn heute noch keiner stattfand; ein fehlgeschlagener Lauf wird am selben Tag nicht wiederholt. Der Lauf löscht in Batches zu 1000 perid IN (SELECT ... LIMIT), danachwal_checkpoint(TRUNCATE)(Fehler nur geloggt), ein Audit-Eintragretention.run. - UI:
/prompts/{name}(alle),/prompts/{name}/edit|draft|versions/{n}/activate|test(Admin),/settings/retention(generische Settings-Section mitextra_template) undPOST /retention/run. - Offene Annahmen: Ollama-Retries im Testbereich wie im Betrieb (Request kann bis zu 3 x Timeout dauern); der Setup-Assistent zeigt Aufbewahrung wie Alarmierung als "erledigt", weil kein Pflichtfeld existiert.
Weiterer Weg (entschieden)
Reihenfolge: Testbasis, erster Lauf im Test-LXC (pytest), Korrekturrunde, Phase 6, Phase 6b, Phase 6c, Phase 8, Abnahme, Parallelbetrieb, Tag v0.1.0. Verfeinerungen passieren am Live-System, nicht vorab.
- Testbasis:
pytestmanuell im Test-LXC (Dev-venv mitrequirements-dev.txt), dazu.gitea/workflows/test.ymlfür einen späteren Gitea-Actions-Runner. Logging als Zeilenlog nach journald, Level überCHECKBOT_LOG_LEVEL(Default INFO), nie Secrets in Logs (Test für die Adapter). Zeitzone überCHECKBOT_TZim Setup-Skript, DefaultEurope/Berlin. - Phase 6: Alarm- und Agenten-Prompt versioniert mit Rollback und Testbereich; der Schutzhinweis gegen Prompt Injection wird immer fest angehängt und ist nicht editierbar. Retention einstellbar: Alarmverlauf/Zustellungen 90 Tage, Agenten-Läufe 90 Tage,
state_log30 Tage, Audit 1 Jahr, abgelaufene Mutes 7 Tage, tägliches Aufräumen mit Log der gelöschten Zeilen. Erinnerungen für offene CRIT-Probleme, Standard aus, nicht bei Downtime/Mute. - Phase 6b (Trigger-Regeln, Routing-Seite wird "Regeln"): Zusatzfelder pro Regel: KI-Hinweistext (nur Zusatzkontext hinter dem festen Schutzhinweis), KI an/aus, Erinnerungsintervall (nur CRIT), laut/leise (Telegram
disable_notification). Zusatzbedingungen: Plugin-Output enthält Text (Teilstring, keine Regex wegen ReDoS) und Mindestdauer in Minuten. Keine Checkmk-Labels/Tags. Beispiel: Service*zpool*, ab WARN, eigener Raum, eigener KI-Hinweis. - Phase 6c (Betriebsrobustheit, nach 6b, vor 8):
- Baseline beim Erstlauf: Zustand still merken, eine Zusammenfassung ohne KI (Anzahl, 10 kritischste); Button in der UI setzt die Baseline neu.
- Bestätigte Probleme (Acknowledged in Checkmk) werden mit Grund "bestätigt" unterdrückt, bei Verschlechterung wieder gemeldet. Regel für alle Unterdrückungen: Recovery nur, wenn das Problem vorher gemeldet wurde.
- Host-Alarme: Hosts mitabfragen, "Host DOWN" als eigener Alarm, Service-Alarme des Hosts mit Grund "Host down" unterdrücken, Recovery bei Rückkehr.
- Checkmk nicht erreichbar: nach 3 Fehlzyklen eine Meldung "Checkmk nicht erreichbar seit HH:MM" an die Standardziele, bei Rückkehr eine zweite; währenddessen keine Recoveries.
- Sendelimit 15 Nachrichten pro Minute und Ziel, Überschuss als Sammelmeldung pro Minute, alles bleibt im Verlauf.
- Datenschutz pro Ziel: Inhalt voll oder kurz (nur Host, Service, Status, ohne Plugin-Output und KI-Text), Standard voll, Entscheidung am Live-System.
- Optionale
ui.base_url: Alarme enthalten Link "Details" auf den Verlauf (Telegram-Button, Matrix-Text), nur wenn gesetzt.
- Standardfall ist der ZFS-Zpool-Alarm:
- Vorinstallierte, aktive Regel "ZFS Zpool" (kommt mit Phase 6b): Service-Glob
*zpool*(Groß/Klein egal), ab WARN, Standardziele, KI-Hinweis mit Prüfschritten (zpool status -v, Scrub-Stand, Fehlerzähler, SMART der betroffenen Platte, läuft ein Resilver), Erinnerung bei CRIT alle 4 h. In der UI editier- und löschbar. Echter Service-Name und Beispiel-Output stehen noch aus, Glob und Texte danach anpassen. - Haupt-Testszenario in E2E und Abnahme:
checkbot-testbekommt einen lokalen Check, der einen Zpool simuliert und per Datei zwischen ONLINE, DEGRADED und FAULTED schaltet (DEGRADED ergibt WARN, FAULTED ergibt CRIT, ONLINE ergibt Recovery). Dazu ein Zpool-Beispielalarm im Prompt-Testbereich. - Das Checkmk-Plugin bzw. der lokale Check für Zpool kann später angepasst werden, ohne Bot-Änderung: stabiler Service-Name (
Zpool <pool>), Schlüsselwort und wichtigste Details in der ersten Ausgabezeile (nur diese erreicht Chat und KI). Optional später:long_plugin_outputmitlesen (kleine Änderung an der Spaltenliste inapp/checkmk.pyund Kürzung im Prompt); Perfdata als Trigger-Bedingung nur, wenn sich der Bedarf am Live-System zeigt.
- Vorinstallierte, aktive Regel "ZFS Zpool" (kommt mit Phase 6b): Service-Glob
- Zpool-Referenz aus dem Live-System: Service heißt
zpool status(Host z.B.kosext100.krs.local), WARN-Output beginnt mit "RaidZ: One or more devices has experienced an unrecoverable error. ..." (ca. 270 Zeichen, ohne Geräte- und Fehlerzahlen). Der Glob*zpool*passt. Dieser Output dient als Vorlage für den simulierten Testcheck und den Beispielalarm im Prompt-Testbereich. - Phase 9 (nach der Abnahme, optional, in der UI abschaltbar): Statusberichte nach Zeitplan. Das Modell schreibt aus dem aktuellen Zustand und den Änderungen seit dem letzten Bericht: Zähler (DOWN-Hosts, CRIT, WARN, UNKNOWN), neu, weiter offen, erholt, bestätigt, in Downtime. Der Zeitplan ist in der UI einstellbar: mehrere Uhrzeiten, Wochentage, Zeitzone des LXC, Ziele, Inhalt voll oder kurz. Dazu ein Button "Bericht jetzt senden" und der Befehl
/report. Der Bericht merkt sich den Stand des letzten Berichts (Vergleich "seit 18:05"). Verfeinerung am Live-System. - Delta in der Alarmnachricht (Phase 6b):
Eventbekommt Vorzustand und Dauer. Alarmzeile zeigt Übergang und Dauer (WARN → CRIT, seit 18:05 (vorher WARN seit 2 h)), Recovery die Gesamtdauer (wieder OK nach 3 h 12 min (zuletzt CRIT)), Erinnerungenweiter CRIT seit 4 h. - Recovery-Meldungen sind in der UI schaltbar: globaler Schalter
alerts.recovery_enabled(Default an) in der Section "Alarmierung" und pro Regel über die Ereignisarten (kindsenthältrecoveryoder nicht). Greift zusätzlich zur Regel "Recovery nur, wenn das Problem vorher gemeldet wurde". Ist Recovery aus, bleibt sie im Verlauf sichtbar, wird aber nicht gesendet. - Aktionen: Der Bot führt nie Shell-Befehle aus (weder lokal noch per SSH). Webhook pro Regel (POST JSON an Allowlist-URLs, Audit) kommt erst nach der Abnahme.
- Phase 8: Integrationstests mit Marker
integration(pytest -m integration), Zugangsdaten per Umgebung, nur lesend; Downtime nur mit ausdrücklichem Flag und gegen einen Testhost. Testhostcheckbot-testin Checkmk mit schaltbarem lokalem Check plus halbautomatischestests/e2e/run.py. - Abnahme (
docs/ACCEPTANCE.md): CRIT in Telegram und Element, KI-Analyse binnen 90 s, Recovery ohne KI, Downtime undmuteunterdrücken, Ollama aus ergibt Rohalarm mit "KI nicht verfügbar", Neustart ohne Doppelalarme, Backup und Restore real getestet,/healthim Checkmk grün, Baseline-Zusammenfassung beim Erstlauf, Host-down unterdrückt Service-Alarme, Meldung bei Checkmk-Ausfall, 24 h Dauerlauf ohne Fehler im Journal. - Einführung: erste Woche nur Testziele (separate Gruppe/Raum) per Routing-Regel, danach auf Produktivziele umschalten. Release: Tag
v0.1.0und kurzeCHANGELOG.md. - Bewusst nicht enthalten: Ack, mehrere Checkmk-Sites, weitere Kanäle (Mail/Slack), E2EE in Matrix.