diff --git a/STATUS.md b/STATUS.md index 4301d67..5f8cc03 100644 --- a/STATUS.md +++ b/STATUS.md @@ -120,7 +120,7 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md). | [0026](docs/adr/0026-pruefziele-werden-abgeleitet-nicht-gepflegt.md) | accepted | ADR-0026: Prüfziele werden abgeleitet, nicht gepflegt — und das Ausbleiben der Herleitung alarmiert | | [0027](docs/adr/0027-zuletzt-eigener-meilenstein.md) | accepted | ADR-0027: „Zuletzt" wird ein eigener Meilenstein (M6) | -## Open AARs (8) +## Open AARs (9) - [AAR — Alle geplanten Prüfungen waren dauerhaft rot und meldeten damit nichts mehr](docs/aar/2026-08-18-alle-pruefungen-dauerhaft-rot.md) - [AAR — Nach der Freischaltung sendete Safari ungefiltert weiter](docs/aar/2026-08-18-safari-sendete-ungefiltert.md) @@ -130,6 +130,7 @@ Bedeutung der Meilensteine: siehe [roadmap.md](roadmap.md). - [AAR — rohana lief voll, der ThreadNet-Web-Spiegel riss](docs/aar/2026-08-20-rohana-platte-voll-spiegel-gerissen.md) - [AAR: Clarks Calls und Identitäts-Reset — vier Fehldiagnosen bis zur bekannten Ursache](docs/aar/2026-08-21-clark-calls-reset-vier-fehldiagnosen.md) - [AAR: Deploy der abgeleiteten CVE-Zielmenge (#0106)](docs/aar/2026-08-21-cve-zielmenge-deploy.md) +- [AAR: Die Ausrollstrecke stand eine Stunde — der Push-Spiegel kam nicht durch](docs/aar/2026-08-21-spiegel-ausfall.md) ## Fehlerklassen (18) diff --git a/docs/aar/2026-08-21-spiegel-ausfall.md b/docs/aar/2026-08-21-spiegel-ausfall.md new file mode 100644 index 0000000..1d063b8 --- /dev/null +++ b/docs/aar/2026-08-21-spiegel-ausfall.md @@ -0,0 +1,103 @@ +--- +type: aar +status: open +date: 2026-08-21 +related: + - "docs/design/2026-08-21-cve-remediation.md" + - "docs/issues/0051-cve-remediation-pass.md" +--- + +# AAR: Die Ausrollstrecke stand eine Stunde — der Push-Spiegel kam nicht durch + +**Schwere:** MEDIUM (kein Produktionsausfall; die Auslieferung neuer Stände war +blockiert) · **Dauer:** ~15:52 bis ~16:57 + +## What was planned / expected + +cert-manager sollte in sieben Fassungssprüngen von v1.14.0 auf v1.21.1 gebracht +werden (#0051). Erwartet war: Commit nach git.lab → Push-Spiegel nach rohana → +Flux zieht binnen einer Minute → Prüfung → nächste Stufe. + +## What happened + +Stufe 1 (v1.15.5) lief planmäßig; letzter erfolgreicher Spiegel **15:51:56**. + +Stufe 2 (v1.16.5) wurde gepusht und **kam nie im Cluster an**. Die Warteschleife +lief sechs Minuten ins Leere, bevor sie abbrach. + +Die Kette, Glied für Glied gemessen: + +| Prüfung | Ergebnis | +|---|---| +| Commit auf git.lab? | ja, `d90861e` | +| Flux-Kustomization gesund? | `Ready=True`, angewandte Revision `93155c2` | +| GitRepository-Quelle? | `Ready=True`, Artefakt bei `93155c2` | +| Hat **rohana** den Commit? | **nein** — `93155c2` | +| Push-Spiegel auf git.lab? | **`to_retry`**, Fehler: *„unable to access … port 443 after 134551 ms: Could not connect to server"* | + +Damit war die Ursache benannt: **git.lab erreichte rohana auf 443 nicht.** + +Nachgemessen aus drei Netzen: Von sorbs Netz **und** aus dem Hetzner-Cluster war +`188.245.193.243:443` tot, während `:2248` (SSH) antwortete und Gitea über den +**privaten** Pfad `10.0.0.3` gesund war (Version 1.27.0, 0,068 s). Traefik auf +dem Host lief zu dem Zeitpunkt seit **sieben Tagen** durch — es war kein +Dienstausfall. + +**Ursache laut sorb:** Auf dem Betriebs-Host wurde an `ufw` und `iptables` +gearbeitet. Der öffentliche Web-Zugang war währenddessen zu. + +**Nach der Wiederherstellung des Netzwegs lief der Spiegel trotzdem nicht.** +GitLab hatte nach den Fehlschlägen die Wartezeit hochgesetzt; weder +`enabled=true` noch Aus- und Wiedereinschalten setzte sie zurück. Erst ein +**echtes Push-Ereignis** (ein leerer Commit) stieß ihn an — 15 Sekunden später +`finished`, Flux zog nach, Stufe 2 rollte aus. + +## Why the difference + +**Routing-Klasse: spec issue** — an der Annahme, nicht am Code. + +Der Entwurf zu #0051 kannte den Rückweg jeder Stufe, aber nicht die +Voraussetzung, dass die Strecke überhaupt trägt. Zwischen „Commit gepusht" und +„im Cluster wirksam" liegen bei uns **drei** Glieder — git.lab, der +Push-Spiegel, Flux — und keines davon wird vor einem mehrstufigen Vorhaben +geprüft. + +⚠️ Erschwerend, und das ist die eigentliche Pointe: **Der Cluster hat vom +Ausfall nichts gemerkt.** Seit dem 2026-08-21 (#0088) zeigt ein CoreDNS-Eintrag +`rohana.axion1337.de` clusterintern auf `10.0.0.3`. Der Entwurf dazu warnte +ausdrücklich vor dem **umgekehrten** Fall: *„Fällt 10.0.0.3 aus, ist rohana aus +dem Cluster nicht erreichbar, obwohl der öffentliche Weg funktionieren würde."* +Eingetreten ist das Spiegelbild — öffentlich tot, privat gesund. Der Zeiger hat +den Cluster gerettet und zugleich verdeckt, dass draußen etwas kaputt war. + +## Learnings + +- ⚠️ **Vor einem mehrstufigen Ausrollvorhaben gehört die Strecke selbst + geprüft**, nicht nur der Rückweg. Ein Commit, der git.lab erreicht, ist noch + lange nicht ausgerollt. +- **Ein Push-Spiegel erholt sich nicht von selbst, wenn das Ziel zurückkommt.** + GitLabs Wartezeit-Erhöhung überlebt `enabled=true` und das Aus-/Einschalten; + es braucht ein Push-Ereignis. Ein leerer Commit ist dafür das sauberste + Werkzeug — er ändert den Baum nicht, überspringt also keine Stufe. +- ⚠️ **Bei gestuften Upgrades darf nicht auf Vorrat gepusht werden.** Läge der + Spiegel und stünden fünf Stufen gestapelt im Repo, wendete Flux nach der + Erholung nur den **letzten** Commit an — und übersprang fünf Minor-Fassungen, + was cert-manager ausdrücklich verbietet. Die Regel lautet: erst ausrollen und + prüfen, dann die nächste Stufe pushen. +- **Ein redundanter Pfad verdeckt den Ausfall des anderen**, wenn nichts den + ungenutzten prüft. Der CoreDNS-Zeiger ist richtig und bleibt — aber niemand + merkt, wenn der öffentliche Weg wegbricht. + +## Actions + +- [x] Spiegel wieder in Betrieb (leerer Commit `5507f13`), Flux zog nach. +- [x] cert-manager in sieben Stufen bis v1.21.1 abgeschlossen, jede Stufe + einzeln geprüft. +- [ ] **Offen:** Es gibt **keine Prüfung**, die einen hängenden Push-Spiegel + meldet. Er stand eine Stunde, und aufgefallen ist es nur, weil ich zufällig + in dem Fenster ausrollen wollte. Eine Regel auf `update_status != finished` + oder auf das Alter von `last_successful_update_at` wäre billig — gehört + als Issue vorgeschlagen, nicht hier entschieden. +- [ ] **Offen:** Ebenso wenig meldet etwas, wenn rohanas **öffentlicher** Weg + wegbricht, solange der private trägt. Der CoreDNS-Zeiger macht den Cluster + unempfindlich und damit blind. diff --git a/docs/design/2026-08-21-cve-remediation.md b/docs/design/2026-08-21-cve-remediation.md index 9c56510..272900b 100644 --- a/docs/design/2026-08-21-cve-remediation.md +++ b/docs/design/2026-08-21-cve-remediation.md @@ -437,3 +437,63 @@ neun Kommentarzeilen, also am **Diff vor dem Commit**, nicht am Ergebnis. Soll-Menge") steht kurz vor seinem Nachweis: Die Herleitung läuft stündlich, die letzte lag vor dem Upgrade. Erscheint `2026.8.0` von selbst, ist das letzte offene Kriterium von #0106 erfüllt. + +### Haufen A, zweiter Posten: cert-manager v1.14.0 → v1.21.1 (sieben Stufen) + +**Recherche zuerst.** Die Anleitung lässt nur zwei Wege: *„one minor version at a +time, always choosing the latest patch version"* — oder vollständige +Deinstallation samt Neuinstallation. Gewählt: die sieben Stufen. + +In sieben Fassungssprüngen gibt es **zwei** potenziell brechende Änderungen, +beide gegen unseren Stand geprüft und **gegenstandslos**: + +| Bruchstelle | Stufe | Bei uns | +|---|---|---| +| ACME-Metriken: `path`-Label entfällt zugunsten von `action` | 1.18→1.19 | `certmanager_acme*` wird **nirgends** ausgewertet | +| `cert-manager-edit` verliert `create` auf Challenges/Orders | 1.19→1.20 | **null** Bindungen auf diese ClusterRole | + +**Durchgeführt, jede Stufe einzeln ausgerollt und geprüft:** + +| Stufe | Fassung | Deployments | Zertifikate | +|---|---|---|---| +| 1 | v1.15.5 | 3/3 | 15/15 Ready | +| 2 | v1.16.5 | 3/3 | 15/15 Ready | +| 3 | v1.17.4 | 3/3 | 15/15 Ready | +| 4 | v1.18.6 | 3/3 | 15/15 Ready | +| 5 | v1.19.6 | 3/3 | 15/15 Ready | +| 6 | v1.20.3 | 3/3 | 15/15 Ready | +| 7 | **v1.21.1** | 3/3 | **15/15 Ready** | + +**Ergebnis, am Zielimage vorab gemessen:** CRITICAL **8 → 0**, HIGH **128 → 24** +über Controller, Webhook und CAInjector. + +**Funktionsprobe nach dem letzten Sprung:** Helm `Ready=True`, null +Controller-Fehler in fünf Minuten, alle 15 Zertifikate mit gesetzter +`renewalTime` (die neue Fassung hat sie also gelesen und eingeplant), 15 Orders +im Zustand `valid`, **null** offene Challenges. Es wird nichts neu ausgestellt. + +⚠️ **Unterbrochen von einem Vorfall**, siehe `docs/aar/2026-08-21-spiegel-ausfall.md`: +Zwischen Stufe 1 und 2 stand die Ausrollstrecke rund eine Stunde, weil der +Push-Spiegel nach git.lab → rohana nicht mehr durchkam. + +### Zwei Kandidaten, die nach Messung NICHT angefasst wurden + +**Wiki.js — das „Update" wäre eine Verschlechterung gewesen.** Wir fahren den +gleitenden Tag `2.5`. Es gibt Release-Tags `2.5.275/276/277`, die nach +Versionsnummer neuer aussehen. Gescannt: + +| | CRITICAL | HIGH | +|---|---|---| +| `wiki:2.5` (läuft) | **12** | **114** | +| `wiki:2.5.277` | **40** | **247** | + +Der gleitende Tag zeigt auf einen **neueren Build** als das Release-Tag. Wer +nach Nummer geurteilt hätte, hätte die kritischen Befunde verdreifacht. Das ist +der beste Beleg dafür, warum „vor dem Update das Zielimage scannen" ins Runbook +gehört. + +**Draupnir** — `v3.1.0` ist die neueste Fassung (2026-05-07); `develop` ist kein +Release. Seine 5 als „behebbar" gezählten CRITICAL sind es für uns **nicht**: +Ein Paket-Fix nützt nichts, solange niemand das Image neu baut. ⚠️ Das ist eine +Verfeinerung von Haufen A insgesamt — „behebbar" heißt *Paket hat einen Fix*, +nicht *es gibt ein neueres Image*.