docs: Gate 1 for #0106 — derive the CVE targets, and one open question
Six countable criteria, the first of which is that images.txt stops existing and is not replaced by another maintained file. Coverage of the running estate has to reach 100 percent, measured as a set difference over normalised names, and stay visible as a metric so the gap cannot return quietly. The open question is how far "everything from the registry" reaches. Six repos hold 36 tags; seven are CI artefacts and 25 of the remaining version tags run nowhere. Scanning all of them triples the workload and puts findings about v0.1.0 into the security room — the exact noise this work removes. Three readings are laid out with a recommendation, not a decision. The ledger records the rungs: nothing needed building for the sources. Both image sets already live in the same Prometheus the scanner can reach, and the registry answers an anonymous token. It also records two wrong turns of my own, including reading a registry 401 as "needs credentials".
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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.**
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user