From e316193f9c8f152b19262389f831be2c07e89a5b Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Fri, 21 Aug 2026 12:00:00 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20Gate=204=20for=20#0106=20=E2=80=94=20fi?= =?UTF-8?q?ve=20slices,=20and=20the=20tracer=20proves=20itself=20against?= =?UTF-8?q?=20a=20hand=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Slice 1 changes no behaviour at all: the derivation runs, the metrics appear, and the scanner keeps reading the old list. Its acceptance is a number already known by hand — 24 missing, 2 orphaned, coverage near 0.52. If the derivation disagrees, the derivation is wrong, not the hand count stale. Rolling out is a handover, not a step of mine. Port 2248 on the operating host answers from the matrix node and agent forwarding carries, but the key is not authorised there, so the procedure written after this very stack applies: whoever builds hands over, whoever deploys verifies and writes the AAR. The quantities that procedure demands are in the doc: about 65 targets instead of 29, about 90 registry requests per derivation, and up to 65 possible messages a round where yesterday brought 12. Slice 2 sets the missing-targets rule above the current value on purpose and only pulls it to zero in slice 3, so the room never learns to live with a red alert. Slice 5 is where this can still fail quietly: if cAdvisor only shows what runs, a stopped service is absent from the desired set and coverage still reads 100 percent. --- docs/design/2026-08-21-cve-ziele-ableiten.md | 102 ++++++++++++++++++- docs/ledger/2026-08-21-cve-ziele-ableiten.md | 4 +- 2 files changed, 103 insertions(+), 3 deletions(-) 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 | |