issues: #0078 was already built and delivering — #0106 for what it hides

#0078 asked for a CVE reporting path with its own room, metrics, a dashboard and
aggregated alerts. ADR-0003 decided it on 2026-08-01 and it was built right
after; the issue has described an accomplished state ever since. Measured rather
than read: the bot is joined, 173 messages stand in the security room, five
rules route by label, the dashboard exists.

What the check does not say is which images it never looked at. The target list
is hand-maintained and covers 27 of 51 running images — 52 percent. Missing are
the web client every user loads, both Traefik layers, the registry itself, the
whole observability stack, and the scanner's own image. Two entries are scanned
and run nowhere, so the room carries findings about an image no one uses.

#0106 carries that on, with sorb's requirement: derive the targets from the
stack and the registry, drop the list rather than police it.
This commit is contained in:
Thore Cimbal
2026-08-21 12:00:00 +00:00
parent c64b3290e8
commit b3d961d92d
3 changed files with 146 additions and 5 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
<!-- Generated by scripts/gen_status.py — do not edit. -->
## Issues (48 open, 51 closed)
## Issues (48 open, 52 closed)
Verteilung: M1 2 · M2 16 · M3 4 · M4 11 · M5 15
@@ -12,7 +12,7 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md).
| Issue | Priorität | Status | Title |
|---|---|---|---|
| [0078](docs/issues/0078-gitops-45-cve-meldeweg-v2-metriken-grafana-dashboard-al.md) | medium | in-progress | CVE-Meldeweg v2: Metriken, Grafana-Dashboard, Alerts in eigenen Matrix-Raum |
| [0106](docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md) | high | open | CVE-Scan deckt 52 % — die Zielliste ist handgepflegt und läuft dem Bestand hinterher |
| [0072](docs/issues/0072-gitops-34-dsgvo-datenschutz-compliance-konkretisieren.md) | low | open | DSGVO/Datenschutz-Compliance konkretisieren |
### M2 (16)
@@ -1,13 +1,15 @@
---
type: issue
id: "0078"
status: in-progress
status: done
created: 2026-08-01
milestone: M1
priority: medium
projekt: gitops
gitlab_iid: "45"
related: []
related:
- "docs/adr/0003-cve-meldeweg-aggregiert.md"
- "docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md"
---
# CVE-Meldeweg v2: Metriken, Grafana-Dashboard, Alerts in eigenen Matrix-Raum
@@ -30,4 +32,34 @@ Verweise: #31 (Scan existiert), #22 (release-watch), CFGMON-13 im Backlog (Absen
---
*Migriert aus Gitea `sorb/axion1337.chat-gitops#47` — dort erstellt am 2026-08-01 von sorb.*
<!-- gitea-migration: sorb/axion1337.chat-gitops#47 -->
<!-- gitea-migration: sorb/axion1337.chat-gitops#47 -->
## Abschluss — 2026-08-21
**Das Zielbild ist gebaut und liefert.** Es wurde am 2026-08-01 mit ADR-0003
entschieden und offenbar unmittelbar danach umgesetzt; das Issue beschreibt
seither einen Zustand, der bereits eingetreten war. Nachgemessen, nicht gelesen:
| Punkt | Beleg |
|---|---|
| Eigener Matrix-Raum, `@alerts` eingeladen | Raum `security`, Bot mit `join`, **173 Nachrichten**, letzte 2026-08-20 23:03 |
| CVEs als Metriken | `trivy_vuln_info`, `trivy_last_scan_timestamp` — 29 Ziele im laufenden Prometheus |
| Grafana-Dashboard | `monitoring/grafana/dashboards/security/cve-overview.json` |
| Prometheus-Alertregeln | fünf, sämtlich mit `room: security` |
| Raum-Routing im Empfänger | `matrix-alerts.py`: Label `room=<name>``MATRIX_ROOM_<NAME>` |
| Scan-Ort auf dem Betriebs-Host statt Lab-CI | Compose führt `cve-scan` (Trivy) und `cve-exporter` |
| Aggregation statt eine Nachricht pro CVE | `count by (target, target_type, host)`, CRITICAL sofort / HIGH nach 24 h |
Die im Text noch offene Checkbox „Bot einladen — sorb" war zum Zeitpunkt der
Prüfung längst erledigt.
**Abweichung vom Vorschlag im Issue, bewusst und besser:** Vorgeschlagen war
`trivy_image_vulnerabilities{image,severity}`. Gebaut wurde `trivy_vuln_info`
mit CVE-ID, Severity, `fixed_version` und `first_seen`, aggregiert erst in der
Alarmregel. Damit liegt das Einzel-CVE-Detail im Dashboard, ohne den Raum zu
fluten — genau die Konsequenz, die ADR-0003 verlangt.
⚠️ **Ein Fund aus der Prüfung, der nicht mehr hierher gehört:** Die Zielliste
des Scans ist handgepflegt und deckt **52 %** des Bestands — `threadnet-web:v0.6.0`,
beide Traefik-Schichten und die Registry selbst werden nicht gescannt.
Weitergeführt als **#0106**.
@@ -0,0 +1,109 @@
---
type: issue
id: "0106"
status: open
created: 2026-08-21
milestone: M1
priority: high
area: security
host: cfgmon
related:
- "docs/issues/0078-gitops-45-cve-meldeweg-v2-metriken-grafana-dashboard-al.md"
- "docs/adr/0003-cve-meldeweg-aggregiert.md"
---
# CVE-Scan deckt 52 % — die Zielliste ist handgepflegt und läuft dem Bestand hinterher
Der CVE-Meldeweg funktioniert: Trivy scannt, der Exporter macht Metriken daraus,
fünf Alarmregeln routen aggregiert in den Security-Raum, das Grafana-Dashboard
steht. Alles nachgemessen beim Abschluss von #0078.
**Er scannt nur die falsche Menge.** Die Ziele stammen aus
`monitoring/cve/images.txt` — einer von Hand gepflegten Liste, die beim
Ausrollen nicht mitgezogen wird.
## Gemessen am 2026-08-21
Quellen: `kube_pod_container_info` (Cluster), `container_last_seen{job="operating_cadvisor"}`
(Betriebs-Host), `trivy_last_scan_timestamp` (Scan-Ziele) — alle drei in derselben
Prometheus-Instanz. Schreibweisen vor dem Vergleich normalisiert.
| | Anzahl |
|---|---|
| Images im Cluster | 39 |
| Images auf dem Betriebs-Host | 12 |
| **zusammen im Betrieb** | **51** |
| davon gescannt | **27** |
| **Deckung** | **52 %** |
**24 laufende Images werden nicht gescannt.** Darunter ausgerechnet:
- `rohana.axion1337.de/sorb/threadnet-web:v0.6.0` — der Client, den **jeder
Nutzer im Browser lädt**
- `rancher/mirrored-library-traefik:3.6.10` **und** `traefik:v3.7.9` — beide
Ingress-Schichten, die **alles TLS terminieren**
- `gitea/gitea:1.27.0` — die **Registry selbst**, zugleich Flux' Quelle
- `ghcr.io/requarks/wiki:2.5` — das öffentlich erreichbare Wiki
- der gesamte Beobachtungs-Stack: `prom/prometheus`, `grafana/grafana`,
`grafana/loki`, `prom/alertmanager`, `grafana/alloy`
- `python:3.13-slim` — Basis von `matrix-alerts`, `release-watch` und
`cve-exporter`
- `aquasec/trivy:0.58.2`**der Scanner scannt sich selbst nicht**
- sechs k3s-Bausteine (`klipper-lb`, `local-path-provisioner`, `coredns`,
`metrics-server`, …), `coturn:4.17.2`, `postgres:15-alpine`, `postgres:16-alpine`
**Zwei Ziele werden gescannt, laufen aber nirgends:** `threadnet-web:v0.3.0`
und `coturn/coturn:latest`. Die Meldung vom 2026-08-20 23:03 über HIGH-CVEs in
`v0.3.0` betrifft ein Image, das niemand benutzt — Rauschen, das wie Signal
aussieht.
## Warum das zählt
Es ist die Fehlerklasse „meldet Erfolg, ist aber blind", nur eine Ebene höher:
Nicht die Prüfung ist kaputt, sondern ihre **Zielmenge**. Der Raum füllt sich
zuverlässig mit Meldungen, das Dashboard ist grün gepflegt — und über die drei
am stärksten exponierten Bauteile (Client, Ingress, Registry) steht dort nichts.
Ein leerer Befund und ein nie gestellter Befund sind in dieser Darstellung nicht
unterscheidbar.
Die Drift ist außerdem nicht theoretisch: Die Liste zeigt `v0.3.0`, produktiv
läuft `v0.6.0`. Zwischen beiden liegen drei Fassungen und ein Upstream-Merge.
## Anforderung (Entscheidung sorb, 2026-08-21)
> „Wir können nicht jedes Mal eine Liste aktualisieren, wenn ich den Stack
> aktualisiere, das muss alles automatisch passieren. Noch besser, wenn generell
> alles aus dem Stack und der Registry gescannt wird, nicht nur eine Liste."
Die Zielmenge wird also **abgeleitet, nicht gepflegt**. `images.txt` entfällt
ersatzlos — eine Regel, die Abweichungen einer Liste meldet, wäre nur eine
Krücke für eine Liste, die es nicht geben sollte.
## Was dafür schon da ist
Alle Quellen liegen bereits in **einer** Prometheus-Instanz, die der Scanner
über das vorhandene Compose-Netz erreicht — kein neuer Dienst, keine neuen
Zugangsdaten, kein neuer Netzweg:
| Quelle | Metrik / Ort |
|---|---|
| Cluster | `kube_pod_container_info` |
| Betriebs-Host | `container_last_seen{job="operating_cadvisor", image!=""}` |
| Registry | Gitea auf **demselben Host** wie der Scanner (`10.0.0.3`) |
⚠️ **`job="gameserver_cadvisor"` (20 Images) ist auszuschließen** — game-operating
liegt außerhalb des Auftrags. Die Trennung ist ein Beschriftungsvergleich.
⚠️ **Die Schreibweisen unterscheiden sich** (`docker.io/library/postgres:…`
gegen `clamav/clamav:…`). Ohne Normalisierung meldet jeder Abgleich
Scheinlücken.
⚠️ **Offene Frage für den Entwurf:** Wie weit reicht „alles aus der Registry"?
Alle Tags aller Repos kann eine Vielzahl alter Fassungen bedeuten — mit
entsprechender Scan-Dauer, Cache-Wachstum und Zugriffszahlen bei Docker Hub.
Der Umfang gehört begrenzt und begründet.
## Abgrenzung
Nicht Teil davon: die Behebung der gefundenen CVEs (#0051), der Meldeweg selbst
(#0078, erledigt) und `release-watch` (#0022).