diff --git a/STATUS.md b/STATUS.md index 301113f..4e3f7d5 100644 --- a/STATUS.md +++ b/STATUS.md @@ -82,9 +82,11 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md). | [0090](docs/issues/0090-gitops-59-kein-kubernetes-audit-log-zugriffe-an-der-api.md) | low | open | Kein Kubernetes-Audit-Log — Zugriffe an der API werden nicht protokolliert | -## Active design docs (0) +## Active design docs (1) -_none active_ +| Design | Gate | Title | +|---|---|---| +| [2026-08-21-cve-ziele-ableiten](docs/design/2026-08-21-cve-ziele-ableiten.md) | gate-1 | Design: Die CVE-Zielmenge ableiten statt pflegen (#0106) | ## ADRs (25) diff --git a/docs/design/2026-08-21-cve-ziele-ableiten.md b/docs/design/2026-08-21-cve-ziele-ableiten.md new file mode 100644 index 0000000..f368fbe --- /dev/null +++ b/docs/design/2026-08-21-cve-ziele-ableiten.md @@ -0,0 +1,114 @@ +--- +type: design +status: gate-1 +date: 2026-08-21 +size: L +related: + - "docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md" + - "docs/adr/0003-cve-meldeweg-aggregiert.md" +--- + +# Design: Die CVE-Zielmenge ableiten statt pflegen (#0106) + +## Gate 1 — Product + +### Problem + +Der CVE-Meldeweg funktioniert und meldet zuverlässig — über die Hälfte des +Bestands. Am 2026-08-21 gemessen: **27 von 51 laufenden Images** werden +gescannt, **52 % Deckung**. Nicht gescannt werden unter anderem der Client, den +jeder Nutzer im Browser lädt (`threadnet-web:v0.6.0`), **beide** +Ingress-Schichten, die alles TLS terminieren, die Registry selbst und der +gesamte Beobachtungs-Stack. Der Scanner prüft nicht einmal sein eigenes Image. + +Umgekehrt meldet er CVEs für `threadnet-web:v0.3.0` — ein Image, das nirgends +läuft. + +**Für wen.** Für den Betreiber, der aus dem Security-Raum ableitet, ob die +Plattform verwundbar ist. Heute kann er das nicht: Ein leerer Befund und ein nie +gestellter Befund sehen dort gleich aus. + +**Ursache** ist nicht die Prüfung, sondern ihre Zielliste. `monitoring/cve/images.txt` +wird von Hand gepflegt und beim Ausrollen nicht mitgezogen. Zwischen dem +Listenstand `v0.3.0` und dem produktiven `v0.6.0` liegen drei Fassungen und ein +Upstream-Merge. + +### Abnahmekriterien + +1. **`monitoring/cve/images.txt` existiert nicht mehr.** Kein Ersatz durch eine + andere gepflegte Liste, auch nicht durch eine generierte Datei im Repo. +2. **Deckung des laufenden Bestands = 100 %.** Zählbar als + `|laufend \ gescannt| = 0`, wobei *laufend* die Vereinigung aus + `kube_pod_container_info` und `container_last_seen{job="operating_cadvisor"}` + ist, Schreibweisen normalisiert. Heute: 24 fehlen. +3. **Kein Ziel ohne Grund.** `|gescannt \ (laufend ∪ Registry-Auswahl)| = 0`. + Heute: 2 (`threadnet-web:v0.3.0`, `coturn/coturn:latest`). +4. **Die Deckung ist als Metrik sichtbar und alarmierbar**, nicht nur einmalig + nachgerechnet — sonst verfällt sie wieder unbemerkt, nur ohne Liste. +5. **Ein Ausrollen genügt als Nachweis:** Nach einer neuen `threadnet-web`-Fassung + erscheint sie ohne menschliches Zutun in den Scan-Zielen, die alte + verschwindet. Vorgeführt, nicht behauptet. +6. **`job="gameserver_cadvisor"` bleibt draußen** — game-operating liegt + außerhalb des Auftrags. Zählbar: kein Ziel aus dieser Menge in + `trivy_last_scan_timestamp`. + +### Nicht-Ziele + +- **Die gefundenen CVEs beheben.** Das ist #0051. Hier geht es allein darum, + *wonach* gesucht wird. +- **Den Meldeweg ändern.** Räume, Aggregation, Dashboard und Alarmregeln + bleiben, wie ADR-0003 sie entschieden hat und #0078 sie belegt hat. +- **Andere Scanner-Merkmale.** Keine Secret-Scans, keine Lizenzprüfung, keine + SBOM-Ablage — nur die Zielmenge. +- **game-operating und das Homelab.** Auch dann nicht, wenn ihre Images in + derselben Prometheus stehen. +- **Ein Registry-Aufräumen.** Alte Tags werden hier nicht gelöscht. + +### ⚠️ Die eine Frage, die Gate 1 nicht allein entscheiden kann + +Die Anforderung lautet „alles aus dem Stack **und der Registry**". Der Stack ist +eindeutig. Die Registry ist es nicht: + +| | Zahl | +|---|---| +| Repos | 6 | +| Tags gesamt | 36 | +| davon `sha-*` / `latest-ci` (CI-Artefakte) | 7 | +| verbleibende Fassungs-Tags | 29 | +| davon im Betrieb | 4 | + +**25 Fassungs-Tags laufen nirgends** — `threadnet-web:v0.1.0` bis `v0.5.4`, +dazu sieben Bau-Artefakte. Sie alle zu scannen verdreifacht den Umfang (rund 83 +Ziele statt 29) und erzeugt genau die Sorte Meldung, die dieses Vorhaben +gerade abschafft: Befunde über Images, die niemand betreibt. + +Drei Lesarten, die Gate 2 gegeneinander abwägen muss: + +- **A — alles.** Wörtlich die Anforderung. Vollständig, aber der Security-Raum + bekommt Befunde zu `v0.1.0`. +- **B — Bestand plus die jeweils jüngste Fassung je Repo.** Deckt „was wir + ausliefern" ab, ohne Archiv-Rauschen. 6 zusätzliche Ziele statt 32. +- **C — Bestand plus alles, was rückrollbar ist.** Die Fassungen, auf die ein + Rollback realistisch zielt (etwa die letzten drei je Repo). Zwischen A und B. + +Meine Empfehlung ist **C**, weil ein Rollback-Ziel mit bekannter Lücke der +einzige Fall ist, in dem ein nicht laufendes Image betrieblich zählt. Das ist +eine Empfehlung, keine Entscheidung. + +### Ankündigung + +Der CVE-Scan sucht künftig selbst, wonach er suchen soll. Statt einer Liste, die +jemand beim Ausrollen mitpflegen müsste, leitet er seine Ziele aus dem ab, was +tatsächlich läuft — im Cluster und auf dem Betriebs-Host — und aus dem, was in +unserer Registry liegt. Für den Betreiber heißt das: Was neu ausgerollt wird, ist +ab dem nächsten Durchlauf mitgeprüft, ohne dass jemand daran denken muss; und was +im Security-Raum steht, bezieht sich auf Images, die es wirklich gibt. Die +Deckung wird dabei selbst zur Metrik, damit die Lücke, die es heute gab, nicht +lautlos zurückkehren kann. + +### Keine Oberfläche + +Kein UI-Anteil. Sichtbar wird das Ergebnis im bestehenden +Grafana-Dashboard `security/cve-overview.json` und im Security-Raum. + +> **STOP — Freigabe für Gate 1.** diff --git a/docs/ledger/2026-08-21-cve-ziele-ableiten.md b/docs/ledger/2026-08-21-cve-ziele-ableiten.md new file mode 100644 index 0000000..5e049ff --- /dev/null +++ b/docs/ledger/2026-08-21-cve-ziele-ableiten.md @@ -0,0 +1,55 @@ +--- +type: ledger +date: 2026-08-21 +size: L +status: open +related: + - "docs/design/2026-08-21-cve-ziele-ableiten.md" + - "docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md" +--- + +# Ledger: Die CVE-Zielmenge ableiten statt pflegen (#0106) + +## Gates + +| gate | commit | approval | status | note | +|---|---|---|---|---| +| 1 | | | OPEN | Wartet auf Freigabe; die Registry-Reichweite ist die offene Frage. | + +## Ladder + +| searched | found | outcome | commit | +|---|---|---|---| +| ob #0078 ueberhaupt noch Arbeit ist, bevor ein Entwurf beginnt | das gesamte Zielbild war seit dem 2026-08-01 gebaut und lieferte: Raum, Metriken, Dashboard, fuenf Regeln, Raum-Routing | reused: nichts gebaut — #0078 mit Nachweis geschlossen statt umgesetzt | | +| eine Entscheidung zum Meldeweg, bevor eine eigene getroffen wird | ADR-0003 (angenommen, 2026-08-01) legt Aggregation, Security-Raum und einen Bot bereits fest | reused: der Meldeweg bleibt unberuehrt und wird Nicht-Ziel | | +| eine Quelle fuer die laufenden Images, statt eine zu bauen | `kube_pod_container_info` (39) und `container_last_seen{job="operating_cadvisor"}` (12) liegen in **derselben** Prometheus, die der Scanner ueber das vorhandene Compose-Netz erreicht | reused: kein neuer Dienst, keine Zugangsdaten, kein neuer Netzweg | | +| einen Weg, game-operating auszuschliessen | `job="gameserver_cadvisor"` (20 Images) trennt sauber von `operating_cadvisor` | reused: ein Beschriftungsvergleich statt einer Ausschlussliste | | +| ob die Registry ohne neue Zugangsdaten lesbar ist | anonymer Token-Tanz genuegt: `/v2/token` → Katalog und Tag-Listen; 6 Repos, 36 Tags | reused: die Standard-Registry-Authentifizierung, die der Scanner ohnehin schon fuer das Ziehen macht | | +| ob die rohana-Berichte ueberhaupt frisch sind | alle 29 Berichte gleichmaessig 4,1 h alt — der Scanner zieht aus der Registry erfolgreich | reused: die bestehende Scan-Schleife bleibt, nur ihre Zielbeschaffung aendert sich | | + +## Notes + +⚠️ **Zwei eigene Fehlschluesse in dieser Erkundung, beide durch die naechste +Messung korrigiert.** + +1. Der erste Deckungsvergleich lief **ohne Normalisierung** der Schreibweisen + (`docker.io/library/postgres:…` gegen `clamav/clamav:…`). Nachgerechnet kam + dieselbe Zahl heraus — aber das war Glueck, nicht Methode. Die + Normalisierung ist deshalb Bestandteil der Abnahmekriterien. +2. Aus einem `401` am Registry-Katalog wurde vorschnell „braucht Zugangsdaten" + geschlossen. Eine Registry antwortet auf `/v2/` **immer** mit 401 und einem + Token-Verweis; anonyme Clients holen den Token und kommen durch. Aufgefallen + ist es nur, weil die Scan-Berichte der rohana-Images frisch waren — also + etwas funktionierte, das laut Schlussfolgerung nicht funktionieren konnte. + +**Der erste Vorschlag war eine Kruecke und wurde von sorb verworfen.** Geplant +war eine Alarmregel, die Abweichungen der gepflegten Liste meldet. sorbs +Einwand: eine Liste, die bei jedem Ausrollen nachgezogen werden muss, erzeugt +nur Fehler und Arbeit. Damit entfaellt nicht nur die Regel, sondern die Liste — +und mit ihr die Abweichung, die zu melden waere. + +**Der Umfang ist gemessen, nicht geschaetzt.** Bestand 51 Images (Cluster 39, +Betriebs-Host 12), gescannt 27, Registry 36 Tags in 6 Repos davon 7 +CI-Artefakte. Die volle Lesart der Anforderung ergaebe rund 83 Ziele statt 29 — +Faktor drei. Deshalb steht die Registry-Reichweite als ausdrueckliche Frage in +Gate 1 statt als stille Annahme im Entwurf.