Files
management/docs/issues/0082-gitops-49-cve-alarme-eine-matrix-nachricht-pro-cve-flut.md
T
Thore CimbalandClaude Opus 5 f2d1adb407 docs(issues): close #0082 - its headline was fixed the day it was written
"Alert delivery is disconnected" was true when the issue was filed and untrue by
that evening. I carried it as a production blocker on the strength of the issue text
alone, which was wrong: the rules aggregate per target, save_state runs inside the
loop including the failure branch, the security null route is gone, and coturn is
pinned. All of it verified in the code, not inferred.

What genuinely remained were the two smaller review points, and both are done now.
The exporter no longer erases first-seen timestamps when a report fails to parse -
pruning is limited to targets actually read this round, proven in both directions.
And because TrivyScanStale cannot by construction report a target that never
produced a report, the exporter now emits trivy_reports_total and
trivy_report_read_errors with an alert on each.

Priority corrected from high to medium to match what was actually open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:00:00 +00:00

5.4 KiB

type, id, status, created, milestone, priority, projekt, gitlab_iid, related
type id status created milestone priority projekt gitlab_iid related
issue 0082 done 2026-08-01 M1 medium gitops 49

CVE-Alarme: eine Matrix-Nachricht pro CVE flutet den Security-Raum -- Zustellung derzeit stumm

Adoptiert aus gitops#49 (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.

Aus dem Deploy von #47 (2026-08-01). Die Pipeline sammelt Daten, die Alarm-Zustellung ist aber abgeklemmt: in monitoring/alertmanager/alertmanager.yml routet room="security" auf einen Null-Receiver (Commit 2b715ca).

Warum

TrivyCriticalVuln und TrivyHighVuln erzeugen eine Alarm-Instanz pro CVE pro Image. Gemessen am ersten Scan-Durchlauf, bei 14 von 29 Images:

Anzahl
CRITICAL (feuert sofort, kein for:) 59
HIGH (for: 24h) 445

Hochgerechnet auf alle 29 Images grob 120 CRITICAL / 900 HIGH.

group_by: [alertname, instance] legt alle in eine Gruppe -> ein Webhook-POST mit ~120 Alarmen. matrix-alerts.py schickt daraus eine Matrix-Nachricht pro Alarm, sequenziell.

Was es zur Schleife macht

save_state() steht in do_POST hinter der Sende-Schleife. Sobald ein Send fehlschlaegt -- Synapse rate-limitet rc_message per Default nach ~10 Nachrichten mit 429 -- fliegt die Exception, der State wird nicht gespeichert, der Receiver antwortet 502. Alertmanager wiederholt daraufhin die komplette Gruppe, und die Fingerprint-Deduplizierung (if fp in state: continue), die genau das verhindern soll, ist beim Retry noch leer. Das wiederholt sich, statt einmalig durchzulaufen.

Zum Scharfschalten noetig

  1. Zustellung buendeln. Entweder matrix-alerts.py auf eine Sammelnachricht pro Webhook-Batch umbauen (die fuenf Pflichtfelder je CVE als eine Zeile -- bleibt vollstaendig), oder die Regeln auf count by (target, severity) aggregieren und die CVE-Details im Dashboard lassen.
  2. State inkrementell speichern, nach jedem erfolgreichen Send, plus 429-Behandlung mit Retry-After.

Danach die room="security"-Route aus alertmanager.yml entfernen.

Kleinere Punkte aus demselben Review

  • TrivyScanStale kann ein Image, das nie erfolgreich gescannt wurde, nicht melden: ohne ersten Report gibt es keine Serie, an der time() - trivy_last_scan_timestamp haengen koennte. Ein dauerhaft fehlschlagendes Image bleibt still; TargetDown deckt nur den toten Exporter ab.
  • Der Exporter prunt den First-Seen-State bei jedem Scrape. Ein transienter Lesefehler (except: continue) loescht die Erstfund-Zeitstempel des betroffenen Targets dauerhaft.
  • coturn/coturn:latest ist als einziges Image ungepinnt (schon in #47 notiert).

Nicht betroffen

Scanner, Exporter, Scrape-Job und Dashboard laufen und sind verifiziert -- Exporter-Last 0,4 s pro Scrape fuer 29 Reports, unkritisch bei 15 s Intervall. Details im monitoring/README.md.


Migriert aus Gitea sorb/axion1337.chat-gitops#51 — dort erstellt am 2026-08-01 von sorb.

Richtigstellung + Abschluss 2026-08-19

Die Kernaussage des Issues war überholt. „Die Alarm-Zustellung ist abgeklemmt" stimmte am Tag der Erstellung — und wurde noch am selben Tag behoben, ohne dass das Issue geschlossen wurde. Ich habe es zwischenzeitlich als Produktionsblocker geführt („CVE-Alarme werden gar nicht zugestellt"); das war falsch, nachgeprüft am Code:

Forderung Stand
Zustellung bündeln Weg 2 gewählt: Regeln aggregieren per count by (target, target_type, host), Details im Dashboard (alerts.yml)
State inkrementell speichern save_state() steht in der Schleife, auch im Fehlerzweig; dazu time.sleep(1) gegen rc_message
room="security"-Null-Route entfernen In alertmanager.yml nicht mehr vorhanden; der Kommentar dort hält die Historie fest
coturn:latest pinnen gitops b4650dc

Alles vier per Commit ff87cb2 (2026-08-01) bzw. gitops.

Heute erledigt: die beiden verbliebenen Review-Punkte

Der Exporter löschte Erstfund-Zeitstempel bei jedem Lesefehler. first_seen wurde bei jedem Scrape auf das reduziert, was gerade gesehen wurde — und ein Bericht, der sich nicht parsen ließ, wurde per stillem continue übersprungen. Seine Findings kamen damit nicht in seen_keys, ihre Zeitstempel waren dauerhaft weg, und „erstmals gesehen" fing danach bei jetzt an. Gemeldet hat das nichts.

Geprunt wird jetzt nur noch für Targets, deren Bericht in diesem Durchgang wirklich gelesen wurde. Beidseitig gegen eine Wegwerf-Ablage belegt, nicht argumentiert: ein unlesbarer Bericht lässt seinen Eintrag stehen (read_errors 1), ein lesbarer Bericht ohne das Finding räumt ihn weiterhin ab.

TrivyScanStale hat eine Blindstelle, die es prinzipiell nicht schließen kann: Ein Target ohne je erfolgreichen Bericht hat keine Serie, an der time() - … hängen könnte — es bleibt still, egal wie lange es kaputt ist. Dafür verlassen den Exporter jetzt zwei Zahlen (trivy_reports_total, trivy_report_read_errors) mit je einer Regel: TrivyReportUnreadable (>0 für 30 min) und TrivyNoReports (==0 für 1 h). Das schließt die Lücke so weit, wie sie ohne Soll-Liste der erwarteten Targets zu schließen ist — eine solche Liste wäre der nächste Schritt, ist aber ein eigener Umfang.

Commit 9a10615 in threadnet-operating.