From 53e3b270e0c7475e65395a85d2e956b67f74c287 Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Fri, 9 Oct 2026 12:53:24 +0000 Subject: [PATCH] dev: Vorgehen bei sensiblen Daten in der Historie dokumentiert, Roadmap 1.1/1.2 erledigt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - git-deploy.md Abschnitt G: Rotieren, Sichern, Umschreiben, Prüfen, Hash-Zuordnung, Push, Server, Forgejo/NAS, lokales Aufräumen - Protokoll: Speicherbereinigung in Forgejo und auf dem NAS erledigt, keine weiteren Klone, Live-Schlüssel bereits rotiert - Agenten-Regel: keine personenbezogenen Daten oder Schlüssel ins Repository - Roadmap: 1.1 und 1.2 erledigt, Context7 und lokales MySQL-Root unter 1.3 Co-Authored-By: Claude Opus 5.5 --- .ai/rules/general.md | 6 ++++ dev/betrieb/git-deploy.md | 74 ++++++++++++++++++++++++++++----------- dev/roadmap.md | 6 ++-- 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/.ai/rules/general.md b/.ai/rules/general.md index 51b524f8..b2102565 100644 --- a/.ai/rules/general.md +++ b/.ai/rules/general.md @@ -13,3 +13,9 @@ Auf `global-mysql` liegen die Datenbanken mehrerer Projekte, und Container-Umgeb - Tests, `migrate`, `migrate:fresh`, `db:wipe` oder Seeder eines anderen bzw. frisch angelegten Laravel-Projekts in diesem Container nur mit `DB_CONNECTION=sqlite DB_DATABASE=:memory: DB_URL=` davor ausführen, z. B. `DB_CONNECTION=sqlite DB_DATABASE=:memory: DB_URL= php artisan test`. - Vorher prüfen, dass `phpunit.xml` dort SQLite `:memory:` mit `force="true"` setzt (siehe `dev/git-deploy-monitoring/02-fehlerprotokoll-glitchtip.md`, „Testdatenbank absichern“). - `php artisan migrate` in diesem Projekt erst nach `php artisan migrate:status`: Stehen unerwartet viele Migrationen aus, ist die Datenbank nicht die erwartete — anhalten und nachfragen. + +## Keine personenbezogenen Daten oder Schlüssel ins Repository +Datenbank-Dumps, Produktions-Logs, Postman-Collections, Adresslisten, `.env`-Inhalte und API-Schlüssel nie committen, auch nicht unter `dev/` oder im Archiv. Am 12.05.2026 wurden zwei Produktiv-Dumps committet; Enttracken half nicht, erst die Historienbereinigung vom 09.10.2026 hat sie entfernt (alle Hashes neu, Force-Push, Server-Abgleich). Deshalb: +- Vor jedem `git add` prüfen, ob neue Dateien solche Daten enthalten; im Zweifel nachfragen. Solche Arbeitsdateien gehören ins Scratchpad und werden nach Gebrauch gelöscht. +- Ist es doch passiert: nicht nur löschen, sondern Betreiber informieren und nach `dev/betrieb/git-deploy.md`, Abschnitt G vorgehen (zuerst Schlüssel rotieren). +- Die Historie nie ohne ausdrückliche Freigabe umschreiben oder force-pushen. diff --git a/dev/betrieb/git-deploy.md b/dev/betrieb/git-deploy.md index 5799df46..736f89f1 100644 --- a/dev/betrieb/git-deploy.md +++ b/dev/betrieb/git-deploy.md @@ -216,6 +216,58 @@ Was bei der Umwandlung (B) auftrat, für den Fall, dass ein weiterer Server so u Aufräumen nach ein paar stabilen Tagen: `~/deploy-v1.1.0.tgz`, `~/backup-vor-v1.1.0.tgz`, `~/backup-vor-git.tgz`, `~/vor-git/`. +## G — Sensible Daten aus der Historie entfernen + +Wurden Kundendaten, Dumps, Logs oder Schlüssel committet, reicht Löschen oder +Enttracken nicht: Die Datei bleibt in jedem Klon und auf jedem Remote +abrufbar. Vorgehen, erprobt am 09.10.2026 (Protokoll unten): + +1. **Schlüssel zuerst rotieren.** Was einmal gepusht war, gilt als bekannt, + auch nach der Bereinigung. +2. **Sichern:** Branches, die nur auf einem Remote liegen, als lokale Branches + anlegen, dann `git bundle create /vor-filter.bundle --all`. Das + Bundle enthält die Daten noch und wird am Ende gelöscht. +3. **Umschreiben** aller Branches und Tags (`git filter-repo` ist besser, fehlt + aber im Dev-Container): + + ```bash + FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f \ + --index-filter 'git rm -r --cached --ignore-unmatch --quiet ""' \ + --tag-name-filter cat -- --branches --tags + ``` + +4. **Prüfen:** Dateibaum (`git rev-parse ^{tree}`) und Commitzahl + (`git rev-list --count `) je Branch und Tag vorher und nachher gleich; + `git rev-list --objects --branches --tags | grep ''` ist leer. +5. **Hash-Zuordnung** alt → neu ins Archiv legen, solange die alten Commits + lokal noch vorhanden sind (Abgleich über Autor- und Committerzeit, Autor, + Betreff). Doku und Commit-Nachrichten nennen weiter die alten Hashes. +6. **Push vom Host** (der Dev-Container hat keinen SSH-Schlüssel), je Remote: + `git push --force-with-lease ` und + `git push --force --tags `. Lehnt Forgejo ab, kurz den Branch-Schutz + lösen. +7. **Server:** `deploy.sh` holt Tags ohne `--force` und scheitert an neu + gesetzten Tags („would clobber existing tag“). Einmal von Hand: + + ```bash + cd ~/pressekonto.com + git status --porcelain # muss leer sein + git fetch --force --tags --prune origin + git checkout --quiet --detach # gleicher Inhalt, neuer Hash + git reflog expire --expire=now --all && git gc --prune=now + git count-objects -vH # size-pack prüfen + ``` + + `RELEASE` bleibt unverändert, ein Deploy ist nicht nötig. +8. **Forgejo und NAS:** Speicherbereinigung des Repositorys anstoßen. Weitere + Klone löschen und neu klonen. +9. **Lokal aufräumen:** `refs/original/*`, verwaiste Remote-Refs und Refs + anderer Werkzeuge (z. B. `refs/codex/…`, sie hielten am 09.10. die alten + Objekte fest) löschen, dann + `git reflog expire --expire=now --all && git gc --prune=now`. Mit + `git cat-file -e ` nachweisen, dass die Objekte weg sind, dann + das Bundle löschen. + ## Betrieb: PHP-FPM beim Deploy Jeder Reload bricht Anfragen ab, die in dieser Sekunde laufen. Im Nginx-Fehlerlog @@ -266,24 +318,4 @@ Ausfall des Anbieters keinen Deploy abbricht. | 08.10.2026 | `v1.6.0` | Mit Migration (`users.sessions_revoked_at_ms`, Batch 22). Härtung aus Phase 4 des Sicherheitsaudits: „Andere Geräte abmelden“, IP-weites Login-Limit (20 Fehlversuche in 15 Minuten), dauerhafte Session-Sperrmarke, Passwörter ab 12 Zeichen mit Leak-Abgleich, CSP im Melde-Modus (`storage/logs/csp-*.log`), npm `shell-quote`/`source-map-js`. Nach dem Deploy: Header `content-security-policy-report-only` vorhanden, Umami- und GlitchTip-Host in `script-src`/`connect-src`. CSP-Log in einigen Tagen auswerten. | | 08.10.2026 | `v1.6.1` | Vorher auf dem Server `php8.4-imagick` installiert und FPM neu geladen: Die Erweiterung fehlte auf Live, Herkunftsprüfung und KI-Vorab-Check der Bilder liefen seitdem ins Leere. Release: Zeile „PHP-Erweiterung Imagick“ in der Betriebsüberwachung, gebaute Livewire-Anfragen von Scannern (GlitchTip 38, 39) als 400 statt 500/419, Browser-Filter für abgebrochene Alpine-Übergänge (GlitchTip 18). Keine Migration. Nach dem Deploy: Betriebsüberwachung „Imagick ist geladen“. | | 09.10.2026 | `v1.7.0` | Spamschutz für Kontakt-Widget und Registrierung (Cloudflare Turnstile, Inhaltsfilter mit Spam-Quarantäne, Namensregel bei der Registrierung). Bei nicht erreichbarem Cloudflare wird durchgelassen und markiert. Keine Migration. Vorher: Turnstile-Keys in der `.env`, Widget im Cloudflare-Dashboard mit allen drei Live-Hostnamen. Nach dem Deploy: Kontakt-Widget im Browser getestet, Betriebsüberwachung „Spamschutz (Cloudflare Turnstile)“ grün. `contact-messages:quarantine-spam`: 152 Anliegen in die Quarantäne, 130 offen (Rest per Sammelaktion), echte Korrekturanfragen blieben offen. `users:prune-bot-registrations` nach freigegebenem Probelauf ausgeführt, danach `USERS_PRUNE_BOT_REGISTRATIONS=true` und `config:cache`. | -| 09.10.2026 | Historie bereinigt | Die Produktiv-SQL-Dumps vom 23.04. (`dev/migration 2026/sql`, ~290 MB, Kundendaten) lagen seit `5b8bdf4` in der Historie und auf beiden Remotes. Mit `git filter-branch` aus allen Branches und Tags entfernt; Dateibäume und Commitzahlen aller 21 Refs unverändert, **alle Hashes neu** (Zuordnung alt → neu: [`commit-map-2026-10-09.txt`](../archive/commit-map-2026-10-09.txt)). Tags `v1.0.0`–`v1.7.0` neu gesetzt, gleicher Inhalt. Force-Push auf `origin` und `NAS`, danach einmalig der Server-Abgleich unten — ausgeführt, Server `size-pack` 33,4 MiB. Lokal zusätzlich drei Codex-Checkpoint-Refs (`refs/codex/…`) entfernt, die noch auf die alte Historie zeigten. | - -### Einmalig nach der Historienbereinigung (09.10.2026) - -`deploy.sh` holt Tags ohne `--force` und würde an den neu gesetzten Tags -scheitern („would clobber existing tag“). Auf dem Server einmal: - -```bash -cd ~/pressekonto.com -git status --porcelain # muss leer sein -git fetch --force --tags --prune origin -git checkout --quiet --detach v1.7.0 # gleicher Inhalt, neuer Hash — keine Dateiänderung -git reflog expire --expire=now --all && git gc --prune=now -git count-objects -vH # size-pack deutlich unter 300 MB -``` - -`RELEASE` bleibt `v1.7.0`, ein Deploy ist nicht nötig. Danach läuft -`deploy.sh` wie gewohnt. Jeder andere Klon (weitere Rechner) wird neu geklont. -In Forgejo und auf dem NAS die Speicherbereinigung des Repositorys anstoßen, -sonst bleiben die alten Objekte dort bis zur nächsten automatischen -Bereinigung liegen. +| 09.10.2026 | Historie bereinigt | Die Produktiv-SQL-Dumps vom 23.04. (`dev/migration 2026/sql`, ~290 MB, Kundendaten) lagen seit `5b8bdf4` in der Historie und auf beiden Remotes. Mit `git filter-branch` aus allen Branches und Tags entfernt; Dateibäume und Commitzahlen aller 21 Refs unverändert, **alle Hashes neu** (Zuordnung alt → neu: [`commit-map-2026-10-09.txt`](../archive/commit-map-2026-10-09.txt)). Tags `v1.0.0`–`v1.7.0` neu gesetzt, gleicher Inhalt. Force-Push auf `origin` und `NAS`, Server-Abgleich nach G.7 (Server `size-pack` 33,4 MiB), Speicherbereinigung in Forgejo und auf dem NAS, keine weiteren Klone. Lokal zusätzlich drei Codex-Checkpoint-Refs entfernt, die noch auf die alte Historie zeigten; danach 33,5 MiB, beide Dump-Blobs nicht mehr vorhanden. Die betroffenen Live-Schlüssel waren bereits rotiert (Betreiber). Vorgehen allgemein in Abschnitt G. | diff --git a/dev/roadmap.md b/dev/roadmap.md index a781592a..178a6902 100644 --- a/dev/roadmap.md +++ b/dev/roadmap.md @@ -55,9 +55,9 @@ oder Release versehen. | # | Punkt | Quelle | Aufwand | |---|---|---|---| -| ~~1.1~~ | ~~**Produktiv-SQL-Dumps in der Git-Historie.**~~ **Lokal bereinigt 09.10.2026** (`git filter-branch`, alle Branches und Tags, Inhalt unverändert, Hashes neu, Zuordnung in [`commit-map-2026-10-09.txt`](archive/commit-map-2026-10-09.txt)). Force-Push auf `origin` und `NAS` und Server-Abgleich erledigt 09.10.2026 (Server und lokal je 33 MiB statt 313 MiB, Dump-Blobs nicht mehr vorhanden). **Offen:** Speicherbereinigung in Forgejo und auf dem NAS anstoßen, andere Klone neu klonen. | [Release-Plan R6.2](archive/release/entwicklungsplan-release.md) | S [LIVE] | -| 1.2 | **Schlüssel rotieren**, deren Rotation nirgends belegt ist: OpenAI, Google OAuth, Mail-Zugang (K2 vom 14.07.), Context7 (stand in `.mcp.json`, Git-Historie), Connektar-API-Keys (Legacy-Keys liefen über Test-Kanäle), MySQL-Root lokal (Git-Historie). | [optimize 14.07. K2](archive/optimize/optimize-14-07-2026.md), [Deploy v1.1.0 F](archive/betrieb/deploy-v1.1.0.md), [Sicherheitsplan](sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md) | S [EXTERN] | -| 1.3 | **Dev-Container neu erstellen.** `docker-compose.yml` ist bereinigt, im laufenden Container sind `DB_CONNECTION` … `DB_PASSWORD` aber weiter als Umgebungsvariablen gesetzt — genau die Ursache des Datenverlusts vom 07.10. Danach lokalen `APP_KEY` reparieren (33 statt 44 Zeichen, ungültig) und Cloudflare-Testkeys in die lokale `.env`. | [Sicherheitsplan](sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md), [Spamschutz](sicherheit/ENTWICKLUNGSPLAN-SPAMSCHUTZ-2026-10-09.md) | S | +| ~~1.1~~ | ~~**Produktiv-SQL-Dumps in der Git-Historie.**~~ **Erledigt 09.10.2026:** aus allen Branches und Tags entfernt (Inhalt unverändert, Hashes neu, Zuordnung in [`commit-map-2026-10-09.txt`](archive/commit-map-2026-10-09.txt)), Force-Push auf `origin` und `NAS`, Server abgeglichen, Speicherbereinigung in Forgejo und auf dem NAS, keine weiteren Klone. Vorgehen für künftige Fälle: [git-deploy.md, Abschnitt G](betrieb/git-deploy.md). | [Release-Plan R6.2](archive/release/entwicklungsplan-release.md) | — | +| ~~1.2~~ | ~~**Schlüssel rotieren**~~ — **Live-Schlüssel bereits rotiert** (OpenAI, Google OAuth, Mail-Zugang, Connektar; Betreiber, bestätigt 09.10.2026). Offen bleiben nur die Entwicklungsschlüssel unter 1.3: Context7 (stand in `.mcp.json`, Git-Historie) und das lokale MySQL-Root-Passwort (Git-Historie). | [optimize 14.07. K2](archive/optimize/optimize-14-07-2026.md), [Sicherheitsplan](sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md) | — | +| 1.3 | **Dev-Container neu erstellen.** `docker-compose.yml` ist bereinigt, im laufenden Container sind `DB_CONNECTION` … `DB_PASSWORD` aber weiter als Umgebungsvariablen gesetzt — genau die Ursache des Datenverlusts vom 07.10. Danach lokalen `APP_KEY` reparieren (33 statt 44 Zeichen, ungültig) und Cloudflare-Testkeys in die lokale `.env`. Context7-Schlüssel und lokales MySQL-Root-Passwort rotieren (beide in der Git-Historie). | [Sicherheitsplan](sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md), [Spamschutz](sicherheit/ENTWICKLUNGSPLAN-SPAMSCHUTZ-2026-10-09.md) | S | | ~~1.4~~ | ~~**Lokale Dateien mit Personenbezug**~~ — **gelöscht 09.10.2026:** `dev/migration/` (7,4 GB), `dev/archive/migration-2026/sql/` (905 MB), `dev/api/`, `dev/newsletter/`, `dev/2026-10/`, `storage/app/private/restore-company-user-auto-2026-10-07.sql`. | [dev/README](README.md) | — | | 1.5 | **Alte Backups auf dem Server löschen:** `~/deploy-v1.1.0.tgz`, `~/backup-vor-v1.1.0.tgz`, `~/backup-vor-git.tgz`, `~/vor-git/`. | [git-deploy.md](betrieb/git-deploy.md) | S [LIVE] |