From 3e81f401781401c1632131b9f468c3aca7a83b6d Mon Sep 17 00:00:00 2001 From: Thore Cimbal Date: Sat, 1 Aug 2026 12:00:00 +0000 Subject: [PATCH] verfahren: Deploy-Uebergabe als Standardverfahren, AAR zu gitops#47 Nach dem Deploy der CVE-Pipeline (gitops#47) als Verfahren festgehalten. Der Stand war korrekt und gelintet; der Blocker entstand erst aus der Datenmenge, gegen die er lief -- eine Alarm-Instanz pro CVE, real 126 CRITICAL und 1222 HIGH. So etwas faellt in keinem Diff auf, nur beim Messen vor dem Deploy. Neu: - .gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml -- Uebergabe-Issue mit Pflichtfeldern Mengengeruest, vollstaendiges Deploy-Kommando, Verifikation im laufenden Dienst, Aussenwirkung samt Not-Aus, Rollback - verfahren/deploy-uebergabe.md -- Ablauf und Pruefliste - verfahren/aar-vorlage.md -- AAR-Vorlage - verfahren/aar/2026-08-01-cve-pipeline-gitops47.md -- der ausloesende AAR Abgrenzung im README ergaenzt: hosts/ und shared/ halten offene Punkte, verfahren/ haelt, wie wir arbeiten. Die Uebergabe-Issues laufen bewusst hier statt im Projekt-Repo, weil das Verfahren repo-uebergreifend gilt. Co-Authored-By: Claude Opus 5 --- .gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml | 107 ++++++++++++++++++ README.md | 18 +++ verfahren/README.md | 19 ++++ verfahren/aar-vorlage.md | 45 ++++++++ .../aar/2026-08-01-cve-pipeline-gitops47.md | 66 +++++++++++ verfahren/deploy-uebergabe.md | 78 +++++++++++++ 6 files changed, 333 insertions(+) create mode 100644 .gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml create mode 100644 verfahren/README.md create mode 100644 verfahren/aar-vorlage.md create mode 100644 verfahren/aar/2026-08-01-cve-pipeline-gitops47.md create mode 100644 verfahren/deploy-uebergabe.md diff --git a/.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml b/.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml new file mode 100644 index 0000000..8afa254 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml @@ -0,0 +1,107 @@ +name: "Deploy-Übergabe" +about: "Ein gebauter, noch nicht ausgerollter Stand wird zum Deploy übergeben" +title: "[DEPLOY] " +body: + - type: markdown + attributes: + value: | + Für die Übergabe „ich habe gebaut — du rollst aus". + Ausführlich: [verfahren/deploy-uebergabe.md](../src/branch/main/verfahren/deploy-uebergabe.md) + + Die Felder sind die Punkte, an denen Übergaben real schiefgegangen sind. + Wo nichts zutrifft, `-` eintragen statt das Feld zu löschen. + + - type: input + id: stand + attributes: + label: Stand + description: Repo, Branch und Commit, der ausgerollt werden soll + placeholder: "sorb/threadnet-operating @ main, b6007c5" + validations: + required: true + + - type: dropdown + id: testtiefe + attributes: + label: Testtiefe + description: Ehrlich einordnen — „ungetestet" ist eine brauchbare Angabe, „läuft" ohne Beleg nicht + options: + - "ungetestet — nur gelintet / statisch geprüft" + - "teilweise getestet — Details unten" + - "getestet gegen echte Daten" + validations: + required: true + + - type: textarea + id: mengengeruest + attributes: + label: Mengengerüst + description: | + Wenn der Stand irgendetwas erzeugt (Nachrichten, Alarme, Zeitreihen, Dateien, Requests): + Wie viel davon beim ersten Lauf? Geschätzt oder gemessen — bitte dazuschreiben, was von beidem. + Genau hier entstehen die Überraschungen: „ein Alarm pro Fund" ist harmlos bei 5 Funden und ein Ausfall bei 126. + placeholder: | + Alarme: ~1 pro CVE pro Image. An 3 von 29 Images gemessen: 27 CRITICAL / 126 HIGH. + Zeitreihen: ~2000. Matrix-Nachrichten: 1 pro Alarm. + validations: + required: true + + - type: textarea + id: kommando + attributes: + label: Vollständiges Deploy-Kommando + description: | + Inklusive aller Reload-, Restart- und Recreate-Schritte. + Ein `up -d`, das „Running" meldet und nichts tut, ist der häufigste stille Fehlschlag. + render: bash + placeholder: | + cd /opt/ && git pull + cd && docker compose up -d + docker compose up -d --force-recreate + validations: + required: true + + - type: textarea + id: verifikation + attributes: + label: Woran erkennt man, dass es wirklich greift + description: | + Konkrete Prüfung **dort, wo der Dienst liest** — im Container, in der API, in der laufenden Config. + Ein grüner Linter belegt, dass die Datei gültig ist, nicht dass sie geladen wurde. + render: bash + placeholder: | + docker exec prometheus grep -c axion-cve /etc/prometheus/alerts.yml # erwartet: 1 + curl -s localhost:9090/api/v1/rules | grep -c axion-cve # erwartet: >0 + validations: + required: true + + - type: textarea + id: aussenwirkung + attributes: + label: Außenwirkung und Not-Aus + description: | + Was verlässt beim ersten Lauf das System — Matrix-Räume, Mails, Webhooks, fremde APIs? + Und wie schaltet man **nur diesen Kanal** stumm, ohne den ganzen Deploy zu verschieben? + Datensammlung und Zustellung getrennt scharf zu schalten ist fast immer möglich und + macht aus einer Blockade einen normalen Deploy. + placeholder: | + Kanal: Matrix-Raum „Security" über Label room=security. + Stumm: in alertmanager.yml route room="security" -> receiver "null". + validations: + required: true + + - type: textarea + id: rollback + attributes: + label: Rollback + description: Was tun, wenn es schiefgeht — und was davon ist *nicht* zurückholbar (gesendete Nachrichten, externe Writes)? + validations: + required: true + + - type: textarea + id: offen + attributes: + label: Bewusst offen gelassen + description: Bekannte Lücken, Nebenfunde, Abgrenzungen. Verhindert, dass die Gegenseite Zeit mit Nachweisen von etwas verbringt, das schon bekannt ist. + validations: + required: false diff --git a/README.md b/README.md index e616c0f..9ba04b9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Chat-Historie oder einem Kopf verschwindet. ``` hosts/ ein File pro Host, benannt nach dem Hostnamen shared/ Themen, die mehrere Hosts betreffen (DNS-Zone, Mail-Policy) +verfahren/ wiederkehrende Abläufe zwischen Personen und Hosts, plus AARs ``` Faustregel für die Einordnung: **Der Eintrag gehört dorthin, wo die Arbeit @@ -31,6 +32,23 @@ auf CFGMON, dessen Ursache ein blockierter Port auf dem Gameserver ist, gehört |---|---| | DNS-Zone `axion1337.de` und Mail-Policy (IONOS) | [shared/zone-axion1337.md](shared/zone-axion1337.md) | +## Verfahren + +`hosts/` und `shared/` halten **offene Punkte**, [verfahren/](verfahren/) hält +**wie wir arbeiten** — Abläufe, die zwischen Personen oder über mehrere Hosts +hinweg gelten und deshalb an kein einzelnes Projekt-Repo gehören. + +| Verfahren | Inhalt | +|---|---| +| [Deploy-Übergabe](verfahren/deploy-uebergabe.md) | „einer baut, ein anderer rollt aus": Issue-Vorlage, Prüfliste, AAR | + +Zur Deploy-Übergabe gehört die Issue-Vorlage +`.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml` — sie erscheint beim Anlegen eines +Issues in diesem Repo als **Deploy-Übergabe**. Das ist die eine Ausnahme von der +Regel unter „Verhältnis zu Gitea-Issues": die Übergabe-Issues laufen hier, weil +das Verfahren repo-übergreifend gilt; der fachliche Inhalt bleibt im jeweiligen +Projekt-Repo. + ## Konventionen **IDs** sind pro Datei fortlaufend mit einem Präfix, das den Ort benennt: diff --git a/verfahren/README.md b/verfahren/README.md new file mode 100644 index 0000000..aecefff --- /dev/null +++ b/verfahren/README.md @@ -0,0 +1,19 @@ +# Verfahren + +Wiederkehrende Abläufe zwischen Personen und Hosts — dort festgehalten, wo sie +nicht an einem einzelnen Projekt-Repo hängen. + +| Datei | Inhalt | +|---|---| +| [deploy-uebergabe.md](deploy-uebergabe.md) | Ablauf und Prüfliste für „einer baut, ein anderer rollt aus" | +| [aar-vorlage.md](aar-vorlage.md) | Vorlage für den After Action Report nach einem Deploy | +| [aar/](aar/) | Abgelegte AARs, benannt `JJJJ-MM-TT-.md` | + +Die zugehörige Issue-Vorlage liegt unter +`.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml` und erscheint beim Anlegen eines +Issues in diesem Repo als **Deploy-Übergabe**. + +Abgrenzung zum Rest des Repos: `hosts/` und `shared/` halten **offene Punkte**, +dieses Verzeichnis hält **wie wir arbeiten**. Ein Verfahren wird hier nur +aufgenommen, wenn es mindestens einmal an einem echten Vorfall gescheitert +oder bewährt ist — der auslösende AAR wird jeweils verlinkt. diff --git a/verfahren/aar-vorlage.md b/verfahren/aar-vorlage.md new file mode 100644 index 0000000..e501dc5 --- /dev/null +++ b/verfahren/aar-vorlage.md @@ -0,0 +1,45 @@ +# AAR — + +**Datum:** JJJJ-MM-TT · **Host/Stack:** … · **Auftrag:** … + +## 1. Ergebnis + +Was ist live und verifiziert. Was ist bewusst **nicht** live, und warum. +Diese Trennung steht ganz oben — sie ist die einzige Angabe, die jemand +braucht, der nur eine Zeile liest. + +## 2. Befunde + +| # | Befund | Schwere | Status | +|---|---|---|---| +| 1 | … | HIGH / MEDIUM / LOW | abgefangen / behoben / notiert · Issue | + +Schwere nach Auswirkung, nicht nach Aufwand. Zu jedem Befund gehört, **wie er +sichtbar wurde** — falls das nicht offensichtlich ist, siehe Abschnitt 4. + +## 3. Verdachtsfälle mit Entwarnung + +Was geprüft und **nicht** bestätigt wurde, mit dem Messwert. Genauso wichtig +wie die Befunde: verhindert, dass dieselbe Vermutung beim nächsten Mal erneut +Zeit kostet. + +## 4. Was die Befunde ermöglicht hat + +Die Methode, nicht die Chronologie. Wenn ein Befund nur durch eine bestimmte +Prüfung sichtbar wurde, gehört genau die hierher — das ist der Teil, der beim +nächsten Deploy wiederverwendbar ist. + +## 5. Offen + +Was bleibt, wer entscheidet, was blockiert. Mit Issue-Referenz statt +wiederholtem Inhalt. + +--- + +Regeln: + +- Nicht verifizierte Aussagen als solche kennzeichnen — eine Vermutung, die wie + ein Befund aussieht, kostet später mehr Zeit als sie spart (Konvention aus + der [README](../README.md)). +- Zahlen statt Adjektive. „Viele Alarme" ist keine Angabe, „126 CRITICAL" ist eine. +- Kurz. Ein AAR, der nicht gelesen wird, hat keinen Wert. diff --git a/verfahren/aar/2026-08-01-cve-pipeline-gitops47.md b/verfahren/aar/2026-08-01-cve-pipeline-gitops47.md new file mode 100644 index 0000000..bcdefa4 --- /dev/null +++ b/verfahren/aar/2026-08-01-cve-pipeline-gitops47.md @@ -0,0 +1,66 @@ +# AAR — CVE-Pipeline `gitops#47` + +**Datum:** 2026-08-01 · **Host/Stack:** CFGMON, `/opt/threadnet-operating/monitoring` +**Auftrag:** Deploy eines gebauten, als UNGETESTET übergebenen Stands (Trivy-Scanner, +Exporter, Alert-Regeln, Grafana-Dashboard) + +## 1. Ergebnis + +**Live und verifiziert:** Scanner (29/29 Images gescannt), Exporter, Prometheus-Job +`cve_exporter`, Regelgruppe `axion-cve`, Grafana-Ordner *Security* mit CVE-Dashboard. + +**Bewusst nicht live:** die Alarm-Zustellung nach Matrix. `room="security"` routet in +`alertmanager.yml` auf einen Null-Receiver (Commit `2b715ca` in +`sorb/threadnet-operating`). Grund siehe Befund 1. + +## 2. Befunde + +| # | Befund | Schwere | Status | +|---|---|---|---| +| 1 | Eine Matrix-Nachricht pro CVE. 126 CRITICAL landen in **einer** Alertmanager-Gruppe, nach 24 h kommen 1222 HIGH dazu. Dazu steht `save_state()` in `do_POST` hinter der Sende-Schleife: bricht ein Send ab (Synapse rate-limitet nach ~10 mit 429), wird kein State gespeichert, der Receiver antwortet 502, Alertmanager wiederholt die komplette Gruppe — mit leerer Deduplizierung | HIGH | abgefangen, `gitops#51` | +| 2 | `docker compose up -d` aktiviert geänderte Configs nicht. Einzeldatei-Mounts hängen am Inode, `git pull` benennt um. Prometheus lief nach dem Deploy mit alten Regeln — `promtool` fand 9, Prometheus kannte 6, kein Fehler im Log | MEDIUM | behoben via `--force-recreate`, `gitops#52` | +| 3 | `TrivyScanStale` kann ein nie erfolgreich gescanntes Image nicht melden — ohne ersten Report existiert keine Serie, an der `time() - trivy_last_scan_timestamp` hängen könnte | LOW | notiert in `gitops#51` | +| 4 | Der Exporter prunt den First-Seen-State bei **jedem** Scrape. Ein transienter Lesefehler (`except: continue`) löscht die Erstfund-Zeitstempel des Targets dauerhaft | LOW | notiert in `gitops#51` | +| 5 | Grafana-Provisioning für den Security-Ordner fehlte — das Dashboard wäre nie erschienen | — | vom Autor selbst behoben (`cdfadc0`), bevor ausgerollt wurde | + +Gemessene Gesamtlage über alle 29 Images: **126 CRITICAL, 1222 HIGH**, 2710 MEDIUM, +1316 LOW. Spitzenreiter `goauthentik/server:2026.2.3` mit 369 CRITICAL+HIGH. + +## 3. Verdachtsfälle mit Entwarnung + +| Vermutung | Messung | Ergebnis | +|---|---|---| +| Exporter parst 29 JSONs je Scrape → Timeouts bei 15 s Intervall | `collect()` gegen echte Reports | 0,4 s für 29 Reports, ~3100 Zeilen — unkritisch | +| Private Registry `rohana.axion1337.de` braucht Credentials für Trivy | Anonymer Pull | zieht anonym, keine Credentials nötig | +| Zwei down-Targets könnten Folge des Deploys sein | `avg_over_time(up[3h])` | 0.00 — schon 3 h vorher tot, in `hosts/game.md` erfasst | + +`promtool check rules`, `amtool check-config`, `amtool config routes test`, +`docker compose config` und `py_compile` liefen alle sauber. + +## 4. Was die Befunde ermöglicht hat + +Befund 1 wäre in keinem Lint aufgefallen — der Code ist korrekt, das Problem entsteht +erst aus der **Datenmenge**, gegen die er läuft. Sichtbar wurde er durch Messen vor dem +Deploy: Trivy lokal über drei repräsentative Images ergab 27 CRITICAL / 126 HIGH auf 3 +von 29, also die richtige Größenordnung. Die spätere Realität (126 CRITICAL) bestätigte +die Entscheidung. `amtool config routes test` belegte danach, dass die Stummschaltung +nur `room=security` trifft und den normalen Alarmweg unangetastet lässt. + +Befund 2 wurde nur sichtbar, weil die Config **im Container** geprüft wurde +(`docker exec prometheus grep …`) statt auf der Platte. Auf der Platte sah alles richtig +aus, `up -d` meldete `Running`, und ein SIGHUP-Reload lud klaglos den alten Inhalt. + +Diese beiden Punkte sind als Verfahren festgehalten: +[../deploy-uebergabe.md](../deploy-uebergabe.md). + +## 5. Offen + +Richtungsentscheidung zu `gitops#51`, bevor die Alarme scharf gehen: entweder +`matrix-alerts.py` auf eine Sammelnachricht pro Webhook-Batch umbauen (die fünf +Pflichtfelder je CVE als eine Zeile, bleibt vollständig), oder die Regeln auf +`count by (target, severity)` aggregieren und die CVE-Details im Dashboard lassen. +In beiden Fällen zusätzlich: State inkrementell speichern, 429 mit `Retry-After` +behandeln. Danach die `room="security"`-Route entfernen. + +Nebenbefund ohne Handlungsbedarf von hier: `coturn/coturn:latest` ist das einzige +ungepinnte Image (bereits in `gitops#47` notiert). diff --git a/verfahren/deploy-uebergabe.md b/verfahren/deploy-uebergabe.md new file mode 100644 index 0000000..fe6cd36 --- /dev/null +++ b/verfahren/deploy-uebergabe.md @@ -0,0 +1,78 @@ +# Verfahren: Deploy-Übergabe + +Für die Konstellation „einer baut, ein anderer rollt aus". Zweck ist nicht mehr +Prozess, sondern **weniger Rückfragen und weniger stille Fehlschläge**. + +Eingeführt am 2026-08-01 nach dem Deploy der CVE-Pipeline (`gitops#47`), siehe +[aar/2026-08-01-cve-pipeline-gitops47.md](aar/2026-08-01-cve-pipeline-gitops47.md). + +## Ablauf + +1. Wer baut, öffnet ein Issue aus der Vorlage **Deploy-Übergabe** + (`.gitea/ISSUE_TEMPLATE/deploy-uebergabe.yaml`). +2. Wer ausrollt, arbeitet die Prüfliste unten ab und deployt. +3. Wer ausrollt, hängt den **AAR** als Kommentar an dasselbe Issue + (Vorlage: [aar-vorlage.md](aar-vorlage.md)). Bei Befunden ab MEDIUM + zusätzlich als Datei unter [aar/](aar/). + +## Die vier Punkte, die den Unterschied machen + +### 1. Mengengerüst vor dem Deploy + +Bei allem, was etwas erzeugt — Nachrichten, Alarme, Zeitreihen, Requests — +gehört die erwartete Anzahl beim **ersten** Lauf in die Übergabe. Geschätzt ist +in Ordnung, gemessen ist besser; welches von beidem, muss dabeistehen. + +Der Grund: „ein Alarm pro Fund" ist eine völlig unauffällige Zeile im Code und +harmlos bei 5 Funden. Bei 126 ist es ein Ausfall. Der Unterschied steht nirgends +im Diff — er ergibt sich erst aus den Daten, gegen die das Ding läuft. + +Eine Stichprobe reicht: drei repräsentative Elemente von 29 messen und +hochrechnen kostet Minuten und liefert die Größenordnung. + +### 2. Verifikation dort, wo der Dienst liest + +Ein grüner Linter belegt, dass eine Datei **gültig** ist — nicht, dass sie +**geladen** wurde. Diese beiden Aussagen sind bei Bind-Mounts, Caches und +Reload-Semantiken regelmäßig verschieden. + +Also im Container prüfen, in der laufenden API, im tatsächlich geladenen +Regelwerk. Ein Blick auf die Platte beweist nichts über den Prozess. + +### 3. Deploy-Kommando vollständig übergeben + +Inklusive Reload-, Restart- und Recreate-Schritten. Ein `docker compose up -d`, +das `Running` meldet und dabei nichts aktiviert, ist der häufigste stille +Fehlschlag: kein Fehler, kein Log, falscher Zustand. + +Konkret auf dem Monitoring-Stack (CFGMON): Einzeldatei-Mounts hängen am Inode, +`git pull` benennt beim Schreiben um und erzeugt damit einen neuen — der +Container zeigt danach weiter auf die alte Datei. Es braucht +`--force-recreate`. Details: `gitops#52`. + +### 4. Zustellwege stumm schalten statt Deploy verschieben + +Datensammlung und Außenwirkung lassen sich fast immer getrennt scharf schalten. +Wenn der Zustellweg das Risiko ist, wird **er** abgeklemmt — nicht der ganze +Deploy verschoben. + +So läuft die Datensammlung ab sofort, das Dashboard steht, echte Zahlen +ersetzen die Schätzung, und die Entscheidung über die Zustellung fällt auf +Basis von Messwerten statt Vermutungen. Wichtig: die Stummschaltung gehört +committet und dokumentiert, sonst ist sie in zwei Wochen ein Rätsel. + +## Prüfliste für den Ausrollenden + +- [ ] Diff gelesen, nicht nur die Beschreibung +- [ ] Mengengerüst plausibel? Bei Zweifel an einer Stichprobe selbst messen +- [ ] Configs mit den jeweiligen Werkzeugen validiert (`promtool`, `amtool`, + `compose config`, `py_compile` …) +- [ ] Außenwirkung identifiziert — was verlässt beim ersten Lauf das System? +- [ ] Nach dem Deploy **im Container** verifiziert, dass die neue Config aktiv ist +- [ ] Geprüft, ob vorher gesunde Dinge noch gesund sind (keine stille Regression) +- [ ] AAR geschrieben, Folge-Issues angelegt, Stummschaltungen dokumentiert + +## Abgrenzung + +Das Verfahren gilt für Übergaben zwischen Personen. Wer baut **und** ausrollt, +braucht kein Issue — der AAR lohnt trotzdem, sobald es Befunde ab MEDIUM gab.