docs: #0051 closes with two criteria it did not quite meet

Zero criticals without a decision across all fifty-four targets, every one of
them actually measured. What could not be fixed is decided instead: twenty-seven
entries, ninety-two CVE ids, each with a reason and a review date that a rule
watches.

Criterion 2 ends at one rather than zero, because the image that runs nowhere is
the rollback target for the one that does, and prefering it unscanned to keep a
number clean would be the wrong trade. Criterion 3 lags until the next scan
round, since the scanner runs daily and today is the day everything moved. Both
are written down as misses rather than redefined into hits.

The closing numbers are hand-computed against the live inventory, not read off
the dashboard. That is how the six criticals in the new synapse surfaced at all —
the pipeline still holds the report for the version we replaced this evening.

A runbook goes with it, because the expensive parts of this pass were never
trivy: a delivery path that does not report its own halt, a network rule a chart
jump outran, and three indentations I assumed instead of measuring.
This commit is contained in:
Thore Cimbal
2026-08-21 12:00:00 +00:00
parent 3749aa6d3d
commit cdcb33fb4c
8 changed files with 340 additions and 20 deletions
+5 -8
View File
@@ -2,9 +2,9 @@
<!-- Generated by scripts/gen_status.py — do not edit. -->
## Issues (50 open, 53 closed)
## Issues (49 open, 54 closed)
Verteilung: M2 16 · M3 4 · M4 11 · M5 18 · M6 1
Verteilung: M2 16 · M3 4 · M4 11 · M5 17 · M6 1
Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md).
@@ -54,11 +54,10 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md).
| [0100](docs/issues/0100-threadnet-web-13-asset-pfade-tragen-weiterhin-element-themes-e.md) | low | open | Asset-Pfade tragen weiterhin "element" (themes/element/…) |
| [0101](docs/issues/0101-threadnet-call-4-kaputtes-paket-0-19-2-threadnet-6-in-der-regi.md) | low | open | Kaputtes Paket 0.19.2-threadnet.6 in der Registry — Herkunft ungeklärt |
### M5 (18)
### M5 (17)
| Issue | Priorität | Status | Title |
|---|---|---|---|
| [0051](docs/issues/0051-cve-remediation-pass.md) | high | in-progress | CVE-Remediation-Pass: Schwachstellen-Report abarbeiten |
| [0065](docs/issues/0065-gitops-25-k3s-api-security-hardening.md) | high | open | K3s API security hardening |
| [0109](docs/issues/0109-der-weg-vom-commit-ins-cluster-meldet-seinen-stillstand-nicht.md) | high | open | Der Weg vom Commit ins Cluster meldet seinen Stillstand nicht |
| [0058](docs/issues/0058-gitops-14-web-application-firewall-waf.md) | medium | open | Web Application Firewall (WAF) |
@@ -84,11 +83,9 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md).
| [0072](docs/issues/0072-gitops-34-dsgvo-datenschutz-compliance-konkretisieren.md) | low | open | DSGVO/Datenschutz-Compliance konkretisieren |
## Active design docs (1)
## Active design docs (0)
| Design | Gate | Title |
|---|---|---|
| [2026-08-21-cve-remediation](docs/design/2026-08-21-cve-remediation.md) | gate-4 | Design: Den CVE-Bestand abarbeiten (#0051) |
_none active_
## ADRs (27)
+1 -1
View File
@@ -3,7 +3,7 @@ type: aar
status: open
date: 2026-08-21
related:
- "docs/design/2026-08-21-cve-remediation.md"
- "docs/design/done/2026-08-21-cve-remediation.md"
- "docs/issues/0051-cve-remediation-pass.md"
- "docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md"
---
+1 -1
View File
@@ -3,7 +3,7 @@ type: aar
status: open
date: 2026-08-21
related:
- "docs/design/2026-08-21-cve-remediation.md"
- "docs/design/done/2026-08-21-cve-remediation.md"
- "docs/issues/0051-cve-remediation-pass.md"
---
@@ -1,6 +1,6 @@
---
type: design
status: gate-4
status: done
date: 2026-08-21
size: L
related:
@@ -742,3 +742,145 @@ nur ihr Symptom.
⚠️ **Unterbrochen von einem Ausfall der Anmeldung**, den ich selbst verursacht
habe — siehe `docs/aar/2026-08-21-mas-haproxy-netzregel.md`.
---
## Gate 5 — Abnahme
### Haufen A, sechster Posten: die zwei Releases mit schwebendem Fassungsbereich
⚠️ **Der Fund dieses Schritts ist kein CVE, sondern eine Form.** In diesem Repo
pinnt **jeder** HelmRelease eine feste Fassung — mit genau **zwei** Ausnahmen,
`alloy` (`0.x`) und `kube-state-metrics` (`5.x`). Und genau diese zwei waren die
zurückgefallenen: `0.12.6` ist die letzte 0er-Fassung des alloy-Charts, `5.37.0`
die letzte 5er von kube-state-metrics. Der Bereich sah aus wie „bleibt aktuell"
und war eine **Decke**.
Die feste Nummer ist hier die ehrlichere: sie veraltet **sichtbar**.
Vor dem Ausrollen — die Lehre aus dem ESS-Ausfall wenige Stunden zuvor — wurden
beide Fassungen mit den echten Werten gerendert und **die internen Ziele**
verglichen, nicht nur unsere Anpassungen:
| | alloy 0.12.6 → 1.9.0 | kube-state-metrics 5.37.0 → 8.4.0 |
|---|---|---|
| Objekte | identisch | identisch |
| Selektoren | identisch | identisch |
| Dienst/Ports | unverändert | `targetPort 8080` → benannt `http`, selber Port |
| RBAC | `nodes/proxy``nodes/pods` | `endpoints``endpointslices` |
| Image | v1.7.5 → **v1.16.3** | v2.15.0 → **v2.20.0** |
`nodes/proxy` fällt weg — unsere alloy-Konfiguration benutzt es nicht (geprüft,
nicht angenommen). Ergebnis nach dem Ausrollen: beide `Ready=True`, Pods laufen,
und — der Punkt, der zählt — **die Metriken kommen weiter an**: `kube_node_info`
und `kube_pod_container_info` 62 s alt, 56 Container-Serien. Ein Pod, der läuft,
ist noch kein Exporter, der liefert.
**4 CRITICAL** (alloy 2, config-reloader 1, kube-state-metrics 1), dazu
`nginx:1.26-alpine``1.31.4-alpine` im Docs-Server (**2**, gemessen 0/0).
### ⚠️ Ein Beiwagen wäre eine Stunde später still aus der Prüfung gefallen
Das alloy-Chart 1.x nagelt seinen `config-reloader` **per Digest** fest. Bei so
einem Container führt `kube_pod_container_info` unter `image` nur noch den
nackten `sha256:…`; die brauchbare Referenz steht allein in `image_spec` — und
die Herleitung las `image`.
Zwei Schäden auf einmal, beide behoben und beide mit Gegenprobe abgesichert:
1. Der Beiwagen wäre **ungescannt** gewesen — genau das Loch, gegen das #0106
gebaut wurde, wieder aufgerissen durch ein Upgrade statt durch Nachlässigkeit.
2. Der nackte Digest rutschte durch die Normalisierung als Repository `sha256`
mit dem Hex als Tag, landete in `targets.txt`, wäre nie ziehbar gewesen und
hätte `cve_targets_missing` **dauerhaft** um eins erhöht — eine Lücke, die auf
nichts zeigt.
**Gefunden durch Nachmessen, nicht durch Nachdenken.** Der Chart-Sprung war
vorher geprüft, gerendert und verglichen; sichtbar wurde das erst, als die
Zielmenge gegen den *laufenden* Cluster neu gebildet wurde.
### Haufen B: 27 Entscheidungen, 92 CVE-Kennungen
Die Abnahme verlangt „behoben **oder** Eintrag mit Begründung **und**
Review-Datum". Umgesetzt als `monitoring/cve/entscheidungen.json`, nicht als
`.trivyignore`**eine bewusste Abweichung vom Wortlaut des Kriteriums**, mit
Grund:
> Trivy ließe unterdrückte Befunde einfach weg. Danach sähe „0 CRITICAL, weil
> behoben" **genauso aus** wie „0 CRITICAL, weil weggeschaut". Die Absicht des
> Kriteriums — eine Entscheidung ist etwas, das man später wieder prüft — trägt
> nur, solange die Befunde sichtbar bleiben.
Deshalb: jeder Befund bleibt in `trivy_vuln_info`; die Entscheidungen stehen
daneben und werden **gezählt**. Neue Zahlen: `cve_critical_offen`,
`cve_critical_entschieden`, `cve_entscheidungen_abgelaufen`,
`cve_entscheidungen_ohne_befund`, `cve_entscheidungen_lesefehler`, dazu
`cve_entscheidung_gueltig_bis` je Paar. Vier Alarmregeln, mit Regeltests.
Drei Eigenschaften, die nicht wegzukürzen sind:
- **CVE-Kennungen einzeln, keine Pauschale je Image.** Sonst verschluckte eine
einmal getroffene Entscheidung auch jeden **neuen** Befund am selben Image.
Der Lader weist einen Eintrag ohne Kennungen ab.
- **Eine unlesbare Datei lässt alles als OFFEN zählen**, nicht als entschieden.
Andersherum sähe ein Tippfehler aus wie „alles erledigt".
- ⚠️ **Eine leere Soll-Menge ist nicht leer, sondern unbekannt.** Beim Bauen
zuerst falsch herum: Fiel die Herleitung aus, ging `cve_critical_offen` auf 0
— Entwarnung ausgerechnet im Ausfall. Aufgefallen erst beim
Ende-zu-Ende-Lauf, nicht beim Lesen des eigenen Codes.
### Abnahmekriterien
| | Kriterium | Ergebnis |
|---|---|---|
| 1 | Kein CRITICAL auf laufendem Image ohne Entscheidung | ✅ **0 offen** von 92 auf 54 Zielen; **alle 54 gemessen**, keines unbekannt |
| 2 | Haufen C entschieden, nicht gepatcht | ⚠️ **1 statt 0** — siehe unten |
| 3 | Am Dashboard ablesbar | ⚠️ **noch nicht** — siehe unten |
| 4 | Die Methode steht als Runbook | ✅ `docs/wiki/admin/cve-durchgang.md` |
| 5 | Kein Suppress ohne Ablauf | ✅ 27 von 27 Einträgen mit Pruefdatum, per Test und per Alarm erzwungen |
**Zu 2:** Übrig bleibt **ein** CRITICAL auf einem Image, das nirgends läuft:
`axion-backup:v1`. Das ist kein Rest der Aufräumarbeit, sondern Absicht — die
Vorgängerfassung ist das **Rückfallziel** zu v2 und gehört deshalb geprüft. Die
62 aus Gate 1 sind weg. Das Kriterium war zu scharf formuliert; die Abweichung
steht hier, statt die Zahl passend zu rechnen.
**Zu 3:** Die Anlage scannt **einmal am Tag**, die Herleitung läuft stündlich.
Nach einer Update-Welle hängen die Zahlen also bis zu 24 h hinterher: alte
Berichte melden weiter (`cve_targets_orphaned`), neue Ziele haben noch keinen
(`cve_targets_missing`). Beides **meldet sich selbst** — genau dafür sind die
Regeln aus #0106 da. Die Zahlen oben sind deshalb gegen den **laufenden**
Bestand gerechnet: Zielmenge live aus Cluster und Betriebs-Host neu gebildet,
Befunde aus der Anlage plus den lokalen Vorab-Scans der noch ungescannten
Ziele. ⚠️ Ohne diese Handrechnung wären die sechs CRITICAL in Synapse v1.158
**unbemerkt geblieben** — die Anlage hält noch den Bericht zu v1.151.
### Was der Durchgang gebracht hat
| Posten | CRITICAL | HIGH |
|---|---|---|
| Authentik 2026.2.3 → 2026.8.0 | 22 | 456 |
| cert-manager v1.14.0 → v1.21.1 (7 Stufen) | 8 | 104 |
| Flux → v2.9.4-Satz | 14 | 183 |
| ESS 26.4.0 → 26.8.0 | 11 | 97 |
| alloy, kube-state-metrics, config-reloader, nginx | 6 | — |
| Haufen C aus der Zielmenge (#0106) | 61 | — |
| Betriebs-Stack + Python-Basis **(ausstehend)** | 18 | 278 |
⚠️ **Die letzte Zeile wirkt erst nach dem Ausrollen auf dem Betriebs-Host.**
Diese 18 CRITICAL stehen als `art: geplant` mit Pruefdatum **2026-08-28** in der
Entscheidungsdatei — bleibt das Ausrollen aus, meldet sich
`CveEntscheidungAbgelaufen`. Die Entscheidung verfällt, nicht die Erinnerung.
### Nicht erreicht, ausdrücklich
- **HIGH.** Dieser Durchgang war CRITICAL-first, so beschlossen in Gate 1. Der
Bestand ist im Zuge der Updates von 2688 auf rund 1500 gefallen, aber das ist
Beifang, keine Bearbeitung.
- **Die k3s-Komponenten.** 15 CRITICAL auf sechs `rancher/*`-Images sind
entschieden, nicht behoben: sie kommen mit k3s und gehen nur mit einem
k3s-Upgrade. Pruefdatum 2026-09-21.
- **Eigene Images.** `clamav-http-scanner:v1.0.0` und `axion-backup` tragen
Befunde, die ein Neubau auf schlankerer Basis wegnimmt — dieselbe Umstellung,
die die drei Python-Dienste heute auf alpine gebracht hat. Als `geplant`
vermerkt, nicht als erledigt behauptet.
+16 -2
View File
@@ -1,12 +1,12 @@
---
type: issue
id: "0051"
status: in-progress
status: done
created: 2026-08-14
milestone: M5
priority: high
area: security
related: [docs/issues/0025-deploy-uebergabe-cve-alarme-aggregiert-receiver.md, docs/aar/2026-08-01-cve-pipeline-gitops47.md, docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md, docs/design/2026-08-21-cve-remediation.md]
related: [docs/issues/0025-deploy-uebergabe-cve-alarme-aggregiert-receiver.md, docs/aar/2026-08-01-cve-pipeline-gitops47.md, docs/issues/0106-cve-scan-deckt-nur-die-haelfte-zielliste-ist-handgepflegt.md, docs/design/done/2026-08-21-cve-remediation.md]
gitlab_iid: "37"
---
# CVE-Remediation-Pass: Schwachstellen-Report abarbeiten
@@ -61,3 +61,17 @@ harmloseren.
betrieben — er steht nur in der Zielmenge, weil die Registry-Auswahl „die
letzten drei Fassungen je Repo" nimmt und das Repo nur zwei hat. Ein
Registry-Aufräumen wäre hier die billigere Antwort als ein Update.
## Abgeschlossen 2026-08-21
**0 CRITICAL ohne Entscheidung** auf 54 Zielen, alle 54 gemessen. 92 CVE-Kennungen
in 27 Entscheidungen mit Begruendung und Pruefdatum; der Rest ueber Updates
behoben (Authentik, cert-manager, Flux, ESS, alloy, kube-state-metrics, nginx,
Betriebs-Stack). Gate 5 samt der beiden nicht sauber erreichten Kriterien:
[Entwurf](../design/done/2026-08-21-cve-remediation.md). Methode als Runbook:
[CVE-Durchgang](../wiki/admin/cve-durchgang.md).
⚠️ **Die letzten 18 CRITICAL wirken erst nach dem Ausrollen auf dem
Betriebs-Host** (`516641b`, `44cc030`, `422b651`). Sie stehen als `art: geplant`
mit Pruefdatum 2026-08-28 in der Entscheidungsdatei — bleibt das Ausrollen aus,
meldet sich `CveEntscheidungAbgelaufen`.
+35 -6
View File
@@ -2,9 +2,9 @@
type: ledger
date: 2026-08-21
size: L
status: open
status: closed
related:
- "docs/design/2026-08-21-cve-remediation.md"
- "docs/design/done/2026-08-21-cve-remediation.md"
- "docs/issues/0051-cve-remediation-pass.md"
- "docs/aar/2026-08-21-spiegel-ausfall.md"
---
@@ -26,6 +26,7 @@ Nachfrage erfuellt wurde (`docs/wiki/stolpersteine/aar-pflicht-ohne-werkzeug.md`
| 1 | 24ae5b9 | sorb | DONE | Drei Haufen statt eines Vorhabens; Reichweite Haufen C = einengen auf Betriebenes. |
| 2 | 80ee448 | sorb | DONE | Gestufter Authentik-Weg belegt; die Pflicht-Zwischenstufe traegt den ganzen CRITICAL-Gewinn. |
| 3+4 | 70823b4 | sorb | DONE | Zusammengelegt (eine Datei, eine Zeile, keine Signaturen); Fund: Flux haette selbsttaetig zurueckgerollt. |
| 5 | 669e24c | sorb | DONE_WITH_CONCERNS | 0 CRITICAL ohne Entscheidung auf 54 gemessenen Zielen; ⚠️ Kriterium 2 endet bei 1 statt 0 und Kriterium 3 haengt bis zur naechsten Scan-Runde nach - beides im Entwurf begruendet statt passend gerechnet. |
## Slices
@@ -41,6 +42,9 @@ Nachfrage erfuellt wurde (`docs/wiki/stolpersteine/aar-pflicht-ohne-werkzeug.md`
| Flux | fcf3169 | DONE | Vier Controller auf den v2.9.4-Satz; 14 CRITICAL -> 0, 231 HIGH -> 48. |
| Betriebs-Stack | 516641b | DONE_WITH_CONCERNS | Fuenf Fassungszeilen, -14 CRITICAL / -278 HIGH; ⚠️ wirkt erst nach Ausrollen auf dem Betriebs-Host. |
| ESS 26.8.0 | 07ffd0e, 533fcde | DONE | Alle acht Abnahmepunkte belegt, inkl. Call gegen LiveKit v1.12.0 und EICAR-Abweisung; ⚠️ Anmelde-Ausfall durch eigene Netzregel, eigener AAR. |
| alloy + kube-state-metrics + nginx | 6cd6ee6 | DONE | Beide Chart-Bereiche waren Decken, nicht Aktualitaet; vorher gerendert und interne Ziele verglichen, nachher Metrik-Frische geprueft. -6 CRITICAL. |
| Entscheidungen | 44cc030, 422b651 | DONE | 27 Eintraege / 92 CVE-Kennungen, 15 Zusicherungen + 4 Alarmregeln mit Regeltests; ⚠️ bewusst KEINE Trivy-Ignore-Datei, Abweichung im Entwurf begruendet. |
| Digest-Luecke | d5e2995 | DONE | Ein per Digest festgenagelter Beiwagen waere still aus der Zielmenge gefallen - gefunden beim Nachmessen, eine Stunde nach dem Chart-Sprung, der ihn hereinbrachte. |
## Ladder
@@ -57,13 +61,18 @@ Nachfrage erfuellt wurde (`docs/wiki/stolpersteine/aar-pflicht-ohne-werkzeug.md`
| ob die neueste Fassung wirklich die beste ist, bevor sie gesetzt wird | Grafana 13.2.0 raeumt 7 CRITICAL ab, treibt HIGH aber 70 -> 162; 12.4.9 macht 7 -> 1 und 70 -> 3 | reused: die 12er-Reihe statt des Major-Sprungs — gemessen, nicht nach Nummer gewaehlt | 516641b |
| ein Manifest fuer genau unseren Flux-Komponentensatz, statt das Release-Manifest zu nehmen | `install.yaml` traegt 7 Deployments, wir betreiben 4 — das CLI erzeugt den passenden Satz | reused: `flux install --export --components=...` statt Handfilterung von 270 kB YAML | fcf3169 |
| eine Quelle fuer die Einengung von Haufen C, statt einer Ausschlussliste | die Herleitung bildet den laufenden Bestand ohnehin | reused: dieselbe Menge als Filter — keine zweite Liste (ADR-0026) | 4758e35 |
| ob Trivy Ausnahmen mit Ablaufdatum selbst kann, bevor etwas Eigenes entsteht | `.trivyignore.yaml` kennt `statement` und `expired_at` - genau die zwei geforderten Felder | built: trotzdem etwas Eigenes. Trivy LOESCHT unterdrueckte Befunde aus dem Bericht, danach ist "0, weil behoben" von "0, weil weggeschaut" nicht mehr zu unterscheiden. Die Absicht des Kriteriums traegt nur mit sichtbaren Befunden. | 44cc030 |
| ein Format fuer die Entscheidungen, das der Exporter lesen kann | PyYAML gibt es im Exporter-Image nicht (stdlib-only, python:3.13-alpine) - JSON schon | reused: `json` aus der stdlib statt einer neuen Abhaengigkeit; die Randbedingung hat das Format entschieden, nicht der Geschmack | 44cc030 |
| ob die beiden Chart-Bereiche wirklich aktuell sind, statt es anzunehmen | `0.x` deckelt bei Chart 0.12.6 (App v1.7.5), `5.x` bei 5.37.0 (App 2.15.0); aktuell sind 1.9.0 und 8.4.0 | reused: der Pin-Stil, den alle 20 uebrigen Releases dieses Repos schon haben - kein neues Muster, sondern das Ende einer Ausnahme | 6cd6ee6 |
| welche internen Ziele ein Chart-Sprung verschiebt, DIESMAL vorher | beide Renderings verglichen: Objekte und Selektoren gleich, `targetPort` benannt statt beziffert, RBAC endpoints -> endpointslices | reused: `helm template` - dieselbe Methode wie beim ESS-Sprung, aber mit der Frage, die dort gefehlt hat | 6cd6ee6 |
## Notes
**Gate 5 steht aus und hat deshalb KEINE Zeile.** Die Vorlage sagt „one row per
gate as it closes" — eine Zeile fuer ein nicht geschlossenes Gate waere eine
Behauptung ohne Commit, und genau das faengt der Richter ab. Was fehlt: Haufen B
(68 CRITICAL ohne Fix-Version, auf acht Images) und der Rest von Haufen A.
**Gate 5 geschlossen.** Haufen B ist nicht gepatcht, sondern entschieden: 27
Eintraege mit Begruendung und Pruefdatum, 92 CVE-Kennungen. Der Rest von Haufen A
ist gefahren; was im Repo liegt, aber noch nicht ausgerollt ist, steht als
`art: geplant` mit kurzem Datum drin und meldet sich selbst, wenn es liegen
bleibt.
⚠️ **Der erste Commit der Haufen-C-Einengung war ungetestet, und die Gegenprobe
hat es gefangen.** Nach dem Abschalten des Filters blieben alle 22 Zusicherungen
@@ -100,3 +109,23 @@ Vormittag noch direkt auf `synapse-main` zeigte. ESS 26.8.0 stellte den Endpunkt
auf haproxy um. Beide Renderings lagen mir vor — ich hatte sie nur auf unsere
Anpassungen hin verglichen, nicht auf verschobene interne Ziele. Eigener AAR:
`docs/aar/2026-08-21-mas-haproxy-netzregel.md`.
⚠️ **Zwei Kriterien sind NICHT sauber erreicht, und das steht so im Entwurf.**
Kriterium 2 endet bei 1 statt 0 (`axion-backup:v1` laeuft nirgends, ist aber
absichtlich als Rueckfallziel in der Menge), Kriterium 3 haengt bis zur
naechsten Scan-Runde nach. Beides waere leicht wegzurechnen gewesen - die Zahl
passend zu definieren ist in diesem Projekt die teurere Gewohnheit.
⚠️ **Die Zahlen des Abschlusses sind von Hand gegen den LAUFENDEN Bestand
gerechnet**, nicht aus der Anlage abgelesen: Zielmenge live neu gebildet,
Befunde aus der Anlage plus den lokalen Vorab-Scans. Ohne das waeren die sechs
CRITICAL in Synapse v1.158 unbemerkt geblieben - die Anlage haelt noch den
Bericht zu v1.151. Ein Werkzeug, das nur einmal am Tag misst, ist am Tag der
Aenderung das schlechteste Auskunftsmittel ueber diesen Tag.
⚠️ **Der Beiwagen-Fund gehoert in die Reihe der eigenen Fehlerklassen.** #0106
wurde gebaut, damit nichts mehr still aus der Pruefung faellt. Vier Stunden
spaeter hat ein Chart-Sprung genau das wieder erzeugt - nicht durch
Nachlaessigkeit, sondern weil eine Upstream-Entscheidung (Digest statt Tag) die
Form der Daten aenderte. Eine Herleitung ist kein Dauerzustand; sie ist eine
Annahme ueber fremde Datenformate.
+138
View File
@@ -0,0 +1,138 @@
---
type: wiki-page
area: admin
related:
- "docs/issues/0051-cve-remediation-pass.md"
- "docs/adr/0026-pruefziele-werden-abgeleitet-nicht-gepflegt.md"
---
# Runbook: einen CVE-Durchgang machen
Wie ein Durchgang durch den Schwachstellen-Bestand abläuft, damit der nächste
nicht wieder bei der Sortierung anfängt. Aufgeschrieben nach dem ersten
vollständigen Durchgang am 2026-08-21 (#0051), 245 CRITICAL am Anfang.
## 0. Die eine Regel
⚠️ **Vor jedem Update das Zielimage scannen.** Das ist der Unterschied zwischen
„ein Update hilft vermutlich" und einer Zahl. Am 2026-08-21 hat diese eine
Gewohnheit **drei** Updates verhindert, die den Bestand verschlechtert hätten:
| Kandidat | erwartet | gemessen |
|---|---|---|
| Wiki.js `2.5``2.5.277` | besser | **12 → 40 CRITICAL** |
| Grafana 12 → 13.2.0 | besser | CRITICAL 7 → 0, aber **HIGH 70 → 162** |
| cAdvisor v0.49.1 → v0.55.1 | 0 | 5 → 4 |
Der Aufruf, wie ihn die Anlage selbst macht:
```sh
docker run --rm -v "$PWD/trivycache:/root/.cache" aquasec/trivy:0.74.0 \
image --scanners vuln --format json --quiet <image> > ziel.json
```
## 1. Bestand aufnehmen und in drei Haufen teilen
„Remediation-Pass" ist keine Aufgabe, sondern drei. Die Trennung ist das
eigentliche Werkzeug:
| | was es ist | welche Arbeit |
|---|---|---|
| **A** | CRITICAL mit verfügbarer Fix-Fassung | ein Aktualisierungs-Vorhaben |
| **B** | CRITICAL ohne Fix-Fassung | je Fall eine **Entscheidung** |
| **C** | CRITICAL auf Images, die nirgends laufen | **Reichweite**, nicht Patchen |
⚠️ **Haufen C ist eine Falle.** Der Reflex ist, sie zu patchen. Richtig ist zu
fragen, warum sie überhaupt geprüft werden. Am 2026-08-21 waren das 62 CRITICAL
auf drei Bau-Artefakten — verschwunden, ohne dass ein einziger Fix nötig war,
indem die Zielmenge auf Betriebenes eingeengt wurde
([ADR-0026](../../adr/0026-pruefziele-werden-abgeleitet-nicht-gepflegt.md)).
⚠️ **„Behebbar" ist enger, als die Zahl behauptet.** Eine Fix-Fassung im Paket
heißt nicht, dass es ein Image damit gibt. Draupnir v3.1.0 trägt fünf Befunde
mit Fix-Fassung — und v3.1.0 ist die neueste Release. Solche Fälle gehören zu
**B**, obwohl der Scanner sie zu A zählt.
Nützliche Abfragen:
```promql
sort_desc(count by (target) (trivy_vuln_info{severity="CRITICAL"}))
count by (quelle) (cve_target_desired)
cve_critical_offen
```
## 2. Haufen A: aktualisieren, gestuft
1. **Den dokumentierten Weg suchen, bevor gesprungen wird.** Authentik verlangt
die Reihenfolge der Major-Releases, cert-manager „one minor at a time".
Beides stand in der Anleitung; beides hätte ein Sprung verletzt.
2. **Bruchstellen gegen den eigenen Bestand prüfen**, nicht fürchten. Am
2026-08-21 waren 5 von 5 gegenstandslos — geprüft in Minuten, gefürchtet
hätten sie einen Tag gekostet.
3. ⚠️ **Prüfen, ob Flux beim Fehlschlag etwas Eigenes tut.**
`upgrade.remediation.strategy` steht per Vorgabe auf `rollback`, und mit
`retries > 0` ist auch die letzte Rückrollung scharf. Für die Dauer eines
gestuften Sprungs `retries: 0` setzen und danach zurückstellen — sonst rollt
Flux mitten in der Treppe zurück und die Stufen zählen nicht mehr.
4. ⚠️ **Nicht auf Vorrat pushen.** Liegt die Ausrollstrecke, wendet Flux nach
der Erholung nur den **letzten** Commit an und überspringt Zwischenstufen.
Erst ausrollen und prüfen, dann die nächste Stufe pushen
([AAR Spiegel-Ausfall](../../aar/2026-08-21-spiegel-ausfall.md)).
5. ⚠️ **Vor einem Chart-Sprung vergleichen, welche internen Ziele sich
verschieben** — nicht nur, ob die eigenen Anpassungen überleben. Beide
Fassungen mit den echten Werten rendern und Dienstnamen, Selektoren und
Ports gegeneinanderhalten. Genau das wurde beim ESS-Sprung versäumt und hat
die Anmeldung gekostet
([AAR MAS/haproxy](../../aar/2026-08-21-mas-haproxy-netzregel.md)).
⚠️ **Ein schwebender Fassungsbereich ist eine Decke, keine Aktualität.**
`version: "0.x"` sieht aus wie „bleibt aktuell" und hält in Wahrheit unter dem
nächsten Major fest. Am 2026-08-21 waren genau die **zwei** Releases mit einem
Bereich die zwei, die zurückgefallen waren — alle fest gepinnten waren aktuell,
weil eine feste Nummer sichtbar veraltet.
## 3. Haufen B: entscheiden, nicht verstecken
Jeder Fall bekommt einen Eintrag in `monitoring/cve/entscheidungen.json` im
Betriebs-Repo:
```json
{ "ziel": "coturn/coturn:4.17.2", "art": "hingenommen",
"grund": "4.17.2 ist die neueste veroeffentlichte Fassung ...",
"pruefen_am": "2026-11-21", "cves": ["CVE-...", "..."] }
```
- `art`: `hingenommen` (es gibt nichts zu tun) oder `geplant` (die Arbeit steht
aus). Ein `geplant` bekommt ein **kurzes** Datum.
- `cves` **einzeln**, nie pauschal je Image — eine Pauschale verschluckt auch
jeden neuen Befund an demselben Image. Der Lader weist einen Eintrag ohne
Kennungen ab.
- `grund` sagt, **warum es nichts zu tun gibt**, mit der Messung dahinter.
„siehe oben" ist keine Begründung; der Test verlangt Substanz.
⚠️ **Bewusst keine Trivy-Ignore-Datei.** Trivy ließe die Befunde einfach weg —
dann sähe „0 CRITICAL, weil behoben" genauso aus wie „0 CRITICAL, weil
weggeschaut". Entschieden heißt **gezählt, nicht versteckt**: die Befunde
bleiben in `trivy_vuln_info`, und `cve_critical_offen` zählt, was noch niemand
angesehen hat.
## 4. Abschließen
- `cve_critical_offen` muss **0** sein.
- `cve_entscheidungen_abgelaufen` muss **0** sein.
- Vorher/Nachher mit Datum in den Abschluss, und **die Verengung benennen**:
Dieser Durchgang war CRITICAL-first. HIGH ist kein Vorhaben, sondern eine
Kategorie, und bekommt einen eigenen.
- ⚠️ Nach einer Update-Welle hängen die Zahlen bis zu **24 Stunden**
hinterher: die Scan-Runde läuft einmal am Tag, die Herleitung stündlich.
Bis dahin melden `cve_targets_orphaned` und `cve_targets_missing` — das ist
der Übergang, nicht ein Schaden.
## Was diesen Durchgang teuer gemacht hat
Nicht die CVEs. Teuer waren: eine Ausrollstrecke, die ihren Stillstand nicht
meldet ([#0109](../../issues/0109-der-weg-vom-commit-ins-cluster-meldet-seinen-stillstand-nicht.md)),
eine Netzregel, die ein Chart-Sprung überholt hat
([#0108](../../issues/0108-erlaubnisliste-veraltet-still-wenn-das-chart-die-verdrahtung-ver.md)),
und drei Einrückungen, die ich angenommen statt gemessen habe. Wer den nächsten
Durchgang macht, verliert die Zeit an derselben Sorte Sache — nicht an Trivy.
+1 -1
View File
@@ -15,7 +15,7 @@ mit Inhalt. Projektfassung des neckbeard-Index (Original:
| Area | Enthält | Stand |
|---|---|---|
| `admin/` | Betrieb: [cfgmon](admin/cfgmon.md) · [game](admin/game.md) · [matrix](admin/matrix.md) · [overmind](admin/overmind.md) · [Refinement & Retro](admin/refinement.md) · [Stillstandsprüfung](admin/stillstandspruefung.md) · [Textbausteine](admin/textbloecke.md) | belegt |
| `admin/` | Betrieb: [cfgmon](admin/cfgmon.md) · [game](admin/game.md) · [matrix](admin/matrix.md) · [overmind](admin/overmind.md) · [CVE-Durchgang](admin/cve-durchgang.md) · [Refinement & Retro](admin/refinement.md) · [Stillstandsprüfung](admin/stillstandspruefung.md) · [Textbausteine](admin/textbloecke.md) | belegt |
| `deployment/` | [Deploy-Übergabe](deployment/deploy-uebergabe.md) (Definition of Done, Kanonisierungs-Verfahren) · [Wiederherstellung](deployment/restore.md) (Restore-Ablauf Matrix-Plattform) | belegt |
| `architecture/` | [Mirror-Topologie](architecture/mirror-topologie.md) · [Lab-Netz](architecture/lab-netzwerk.md) · [DNS-Zone](architecture/zone-axion1337.md) · [Branding](architecture/branding.md) | belegt |
| `vision/` | Eine Datei je Linie: [axion1337.chat](vision/axion1337-chat.md) · [Homelab](vision/homelab.md) · [ThreadNet](vision/threadnet.md) | belegt |