diff --git a/docs/design/2026-08-21-cve-ziele-ableiten.md b/docs/design/2026-08-21-cve-ziele-ableiten.md index f24161a..914dc32 100644 --- a/docs/design/2026-08-21-cve-ziele-ableiten.md +++ b/docs/design/2026-08-21-cve-ziele-ableiten.md @@ -1,6 +1,6 @@ --- type: design -status: gate-3 +status: gate-4 date: 2026-08-21 size: L related: @@ -367,4 +367,102 @@ steht, wird gelöscht; bei **fehlender** `targets.txt` wird nichts gelöscht. verliert seinen Bericht und damit seine `first_seen`-Historie. Ob das eine Karenz braucht, entscheidet Gate 4 an der Messung. -> **STOP — Freigabe für Gate 3.** +> **Gate 3 freigegeben durch sorb, 2026-08-21.** + +## Gate 4 — Vertical Slices + +### ⚠️ Ausrollen ist eine Übergabe, kein eigener Schritt + +Der Betriebs-Host ist kein Flux-Ziel; dort läuft Compose von Hand. Gemessen am +2026-08-21: `10.0.0.3:2248` ist von MATRIX aus offen, Agent-Weiterleitung +funktioniert, aber der Schlüssel ist dort nicht zugelassen. Es gilt daher das +Verfahren [Deploy-Übergabe](../wiki/deployment/deploy-uebergabe.md), das nach +genau diesem Stack eingeführt wurde: **Wer baut, übergibt; wer ausrollt, prüft +und schreibt den AAR.** + +Jeder Slice trennt deshalb, was **vor** der Übergabe belegbar ist (Tests, +`promtool`, `docker compose config`) von dem, was erst **nach** dem Ausrollen +messbar ist. + +**Mengengerüst für die Übergabe** (Pflicht laut Verfahren, Punkt 1): + +| | heute | nach Slice 3 | +|---|---|---| +| Scan-Ziele | 29 | **~65** (51 Bestand + Registry-Auswahl, überlappend) | +| Dauer einer Runde | unbekannt, < 24 h | Faktor ~2,2 erwartet | +| mögliche Meldungen je Runde | 29 | **~65** — am 2026-08-20 kamen 12 | +| Registry-Anfragen je Herleitung | 0 | **~90** (36 Tags × 2–3) | +| Exporter-Laufzeit je Scrape | 0,4 s / 29 Berichte (AAR) | ~1 s erwartet | + +Geschätzt, nicht gemessen — außer den mit Quelle belegten Zeilen. + +### Slice 1 — Tracer: die Soll-Menge entsteht und wird **nur beobachtet** + +Dünner Pfad von allen drei Quellen bis in die Metriken. Der Scanner bleibt +unangetastet und liest weiter `images.txt`; das Verhalten der Anlage ändert sich +in diesem Slice **nicht**. + +| Aufgabe | Dateien | Aktion | Verify | Done | +|---|---|---|---|---| +| 1.1 | `cve/targets.py` | Herleitung + Normalisierung, stdlib | `python3 -m py_compile` | Modul importierbar | +| 1.2 | `cve/test_targets.py` | Zusicherungen aus Gate 3, gegen **aufgezeichnete** Antworten | `python3 test_targets.py` grün, und einmal absichtlich rot | Jede Zusicherung hat ihren Gegenbeweis | +| 1.3 | `cve-exporter.py` | Zwischenspeicher + fünf `cve_target_*`-Metriken | `py_compile`, lokaler Lauf gegen aufgezeichnete Antworten | `/metrics` trägt die fünf Serien | +| 1.4 | `docker-compose.yml` | Volume `cve_targets`, Umgebung für die drei Quellen | `docker compose config` | Volume rw beim Exporter, ro beim Scanner | + +**Abnahme dieses Slices ist eine Zahl, die ich schon von Hand kenne:** Nach dem +Ausrollen muss `cve_targets_missing` **24** ergeben, `cve_targets_orphaned` +**2** und `cve_target_coverage_ratio` **≈ 0,52** — die Werte der Handmessung vom +2026-08-21. Weicht die Herleitung ab, ist sie falsch, nicht die Handmessung +überholt. Das ist der eigentliche Zweck des Tracers: eine unabhängig gebildete +Zahl gegen eine unabhängig ermittelte halten. + +### Slice 2 — Regeln und Dashboard, weiterhin beobachtend + +| Aufgabe | Dateien | Aktion | Verify | Done | +|---|---|---|---|---| +| 2.1 | `prometheus/alerts.yml` | `CveTargetsMissing`, `CveTargetsOrphaned`, `CveTargetSourceStale` — alle `room: security` | `promtool check rules` | Regeln geladen **im Container** geprüft (AAR-Befund 2) | +| 2.2 | dasselbe | Jede neue Regel einmal absichtlich rot fahren | Alarm erscheint im Security-Raum | Vorgeführt, nicht behauptet | +| 2.3 | `grafana/dashboards/security/cve-overview.json` | Panels „Deckung" und „Frische je Quelle" | Dashboard lädt | Panels zeigen Werte, nicht `No data` | + +⚠️ `CveTargetsMissing` steht anfangs auf 24 und wäre sofort rot. Sie bekommt +deshalb in diesem Slice eine Schwelle oberhalb des Ist-Werts und wird in +Slice 3 auf `> 0` gezogen — sonst gewöhnt sich der Raum an einen roten Alarm. + +### Slice 3 — Der Scanner nimmt die abgeleitete Menge + +| Aufgabe | Dateien | Aktion | Verify | Done | +|---|---|---|---|---| +| 3.1 | `cve/scan-loop.sh` | liest `/targets/targets.txt`; fehlt sie → Runde aussetzen | `sh -n` | Kaltstart ohne Datei scannt nichts und bricht nicht ab | +| 3.2 | `cve/images.txt` | **löschen** | — | Kriterium 1 erfüllt | +| 3.3 | `docker-compose.yml` | Mount anpassen | `docker compose config` | Scanner sieht `/targets` | +| 3.4 | `prometheus/alerts.yml` | `CveTargetsMissing` auf `> 0` | `promtool check rules` | — | + +**Abnahme:** Nach einer vollen Runde `cve_targets_missing` = **0**, +`cve_target_coverage_ratio` = **1,0**, und `threadnet-web:v0.6.0` hat einen +Bericht. Dazu **gemessen** statt geschätzt: Dauer der Runde und Zahl der +Meldungen im Security-Raum. + +### Slice 4 — Ausgefallene Ziele verlieren ihren Bericht + +| Aufgabe | Dateien | Aktion | Verify | Done | +|---|---|---|---|---| +| 4.1 | `cve/scan-loop.sh` | Berichte ohne Ziel löschen — **nur** gegen eine erfolgreich gelesene Zielliste | Test 4.2 | — | +| 4.2 | `cve/test_targets.py` | Gegenkontrolle: **ohne** `targets.txt` wird nichts gelöscht | Test grün und einmal rot gefahren | Der gefährliche Fall ist geprüft, nicht nur der gute | + +**Abnahme:** `cve_targets_orphaned` = **0**, der Bericht zu +`threadnet-web:v0.3.0` ist fort, und der Security-Raum meldet ihn nicht mehr. + +### Slice 5 — Die wackelige Annahme prüfen, Kriterium 5 vorführen + +| Aufgabe | Dateien | Aktion | Verify | Done | +|---|---|---|---|---| +| 5.1 | — | `container_last_seen` gegen `docker compose config` halten | Mengenvergleich | Belegt oder widerlegt, dass ein **gestoppter** Dienst fehlt | +| 5.2 | — | Kriterium 5 vorführen: neue `threadnet-web`-Fassung erscheint ohne Zutun, alte verschwindet | zwei Herleitungen im Abstand | Vorgeführt | +| 5.3 | ggf. Entwurf | Ergebnis von 5.1 als Befund oder als Nicht-Problem festhalten | — | Steht in Gate 5 | + +⚠️ 5.1 ist der Punkt, an dem dieses Vorhaben scheitern kann, ohne es zu merken: +Zeigt cAdvisor nur Laufendes, ist die Soll-Menge unvollständig und die Deckung +meldet trotzdem 100 %. Dann braucht es eine zweite Quelle für den Betriebs-Stack +— und das gehört als Befund in Gate 5, nicht stillschweigend in den Code. + +> **STOP — Freigabe für Gate 4.** diff --git a/docs/ledger/2026-08-21-cve-ziele-ableiten.md b/docs/ledger/2026-08-21-cve-ziele-ableiten.md index 72fb22a..a0424d2 100644 --- a/docs/ledger/2026-08-21-cve-ziele-ableiten.md +++ b/docs/ledger/2026-08-21-cve-ziele-ableiten.md @@ -14,7 +14,8 @@ related: | gate | commit | approval | status | note | |---|---|---|---|---| -| 3 | | | OPEN | Dateien, Signaturen, Zusicherungen, Grenzen; fuenf wackelige Annahmen benannt. | +| 4 | | | OPEN | Fuenf Slices; Ausrollen als Deploy-Uebergabe, Mengengeruest beigelegt. | +| 3 | a0cd02a | sorb | DONE | Dateien, Signaturen, Zusicherungen, Grenzen; fuenf wackelige Annahmen benannt. | | 2 | 2693a2d | sorb | DONE | Herleitung im Exporter statt drittem Dienst; ADR-0026 vorgeschlagen. | | 1 | e10cd5d | sorb | DONE | Sechs Kriterien; Registry-Reichweite = Option C (Bestand + letzte drei je Repo). | @@ -27,6 +28,7 @@ related: | 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 | | +| einen Weg, den Betriebs-Host selbst auszurollen | `10.0.0.3:2248` ist von MATRIX offen und die Agent-Weiterleitung traegt, der Schluessel ist dort aber nicht zugelassen | reused: das bestehende Verfahren `deploy-uebergabe.md` statt eines neuen Zugangs — es entstand 2026-08-01 aus genau diesem Stack | | | einen anonymen Weg an die Tag-Zeitstempel, bevor ein Token eingeplant wird | Giteas `/api/v1/packages` verlangt einen Token — die Registry-API dagegen nicht: Manifest → Config-Blob liefert `created`, anonym, ~90 Anfragen je Runde | reused: die Registry-Authentifizierung, die der Scanner ohnehin macht — Randbedingung "keine neuen Zugangsdaten" bleibt gewahrt | | | einen Ort fuer die Soll-Menge, bevor ein Dienst dafuer gebaut wird | der `cve-exporter` braucht sie fuer die Deckungsmetrik ohnehin und ist ein laufender stdlib-Python-Prozess im selben Netz | reused: bestehender Prozess statt drittem Container — kein `jq`, kein `pip`, keine zweite Wahrheit | | | eine AAR zu genau diesem Stack, bevor Randbedingungen geraten werden | AAR 2026-08-01: `up -d` aktiviert Configs nicht (im Container pruefen), und `TrivyScanStale` kann ein nie gescanntes Image nicht melden | reused: beide Funde als Randbedingung uebernommen statt neu zu entdecken — Befund 3 bestimmt, dass die Deckung aus der Soll-Menge kommen muss | |