# Migration & Secret-Rotation - Runbook Ziel: `melesICUmover` sicher vom **Altserver** auf den **neuen Server** umziehen, alle kompromittierten Zugangsdaten **rotieren**, den Altserver **stilllegen** und die Secrets aus der **Git-History** entfernen. > Kontext: Der Poller haelt keine eigenen Daten - Bilder liegen in Hetzner S3 > (`trapper-meles`), der Zustand in PostgreSQL. "Migration" heisst also: den > **Worker-Container** umziehen und die **Credentials** erneuern. Es gibt keine > Datenmigration. ## Warum rotieren (nicht nur entfernen)? Folgende Secrets lagen im Klartext in `config.yaml` und `.env` **und in der Git-History** (Commits `400f8a5`/`b35394c`) auf `git.meles.eu`. Sie gelten als kompromittiert und muessen ersetzt werden: - S3 Access Key + Secret Key (Hetzner Object Storage) - PostgreSQL-Passwort (`postgres@136.243.41.58:7777`) - DeerMapper API-Key Das Bereinigen des Codes allein schuetzt nicht - die alten Werte bleiben gueltig, bis sie widerrufen/geaendert werden. --- ## Phase 0 - Vorbereitung (kein Ausfall) 1. Auf dem **neuen Server** Docker + Compose pruefen/installieren: ```bash docker --version && docker compose version || curl -fsSL https://get.docker.com | sh ``` 2. Repo auf den neuen Server holen (bereinigter Stand, ohne Secrets): ```bash git clone ssh://git.meles.eu/meles2/melesICUmover.git cd melesICUmover ``` 3. `.env` anlegen (noch NICHT starten): ```bash cp .env.example .env chmod 600 .env ``` ## Phase 1 - S3-Key rotieren (kein Ausfall, additiv) Hetzner erlaubt mehrere S3-Credentials pro Projekt. Neuen Key **zusaetzlich** anlegen, alten vorerst aktiv lassen: 1. In der Hetzner Console -> Object Storage -> neuen S3-Zugang (Access/Secret) erzeugen. 2. In der `.env` auf dem neuen Server `S3_ACCESS_KEY` / `S3_SECRET_KEY` eintragen. ## Phase 2 - DeerMapper API-Key rotieren Nur relevant, wenn `enable_deermapper_api: true` (aktuell `false`). Falls genutzt: neuen Key beim DeerMapper-Betreiber anfordern und in die `.env` eintragen. Sonst Feld leer lassen. ## Phase 3 - Cutover (kurzer, geplanter Ausfall) Der PostgreSQL-Passwortwechsel trennt sofort den Altserver - daher hier gebuendelt: 1. **Altserver-Poller stoppen** (kein Doppelbetrieb auf denselben Bucket/DB): ```bash # auf dem ALTSERVER docker compose down # bzw. docker stop melesicumover ``` 2. **PostgreSQL-Passwort aendern** (Beispiel; besser: eigene DB-Rolle fuer den Dienst): ```sql ALTER USER postgres WITH PASSWORD ''; -- Empfohlen stattdessen: dedizierte Rolle mit minimalen Rechten auf remote_cam.* -- CREATE ROLE icu_mover LOGIN PASSWORD '...'; -- GRANT USAGE ON SCHEMA remote_cam TO icu_mover; -- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA remote_cam TO icu_mover; ``` 3. `PG_DSN` in der `.env` des neuen Servers setzen - **mit `?sslmode=require`** und, sofern moeglich, ueber privates Netz / SSH-Tunnel statt der oeffentlichen IP. 4. **Neuen Server starten und verifizieren:** ```bash # auf dem NEUEN Server docker compose up -d --build docker compose logs -f # auf [RESULT] ...-Zeilen achten ``` Verifikation: - Log zeigt verarbeitete UUIDs mit `result=success`. - In S3 landen neue Objekte unter `icu/processed/` und `icu/thumbnails/`. - `icu/entrance/` wird geleert (Cleanup laeuft). - DB: `SELECT status, count(*) FROM remote_cam.import_job GROUP BY 1;` ## Phase 4 - Altzugaenge widerrufen 1. Alten S3-Key in der Hetzner Console **loeschen** (erst nachdem der neue Server nachweislich laeuft). 2. Sicherstellen, dass der Altserver den neuen DB-Zugang **nicht** kennt. ## Phase 5 - DB-Zugang absichern (Haertung) - PostgreSQL **nicht** auf `0.0.0.0:7777` oeffentlich anbieten. Firewall so setzen, dass nur die IP des neuen Servers (oder ein privates Netz) den DB-Port erreicht. - TLS erzwingen (`sslmode=require`), Hetzner Cloud Firewall bzw. `pg_hba.conf` pruefen. ## Phase 6 - Altserver stilllegen / neu aufsetzen 1. Sicherstellen: keine unersetzten Daten mehr lokal (der Poller hat keine). 2. Container + Images + lokale `.env`/`config.yaml` mit Secrets entfernen: ```bash # auf dem ALTSERVER docker compose down --rmi all --volumes shred -u .env 2>/dev/null || rm -f .env ``` 3. Server neu aufsetzen bzw. deprovisionieren. Falls neu installiert: alte SSH-/Deploy-Keys, die auf `git.meles.eu` Zugriff hatten, am Git-Server widerrufen. ## Phase 7 - Git-History bereinigen (koordiniert, destruktiv) Erst **nach** erfolgreicher Rotation - danach sind die alten Werte ohnehin wertlos, aber sie sollen auch nicht mehr auffindbar sein. > Warnung: Das schreibt die History um und erfordert `--force`-Push. Alle Personen > mit Klon muessen anschliessend neu klonen. Vorher mit dem Team abstimmen. Mit [git filter-repo](https://github.com/newren/git-filter-repo) (empfohlen): ```bash # frisches Spiegel-Klon als Arbeitskopie git clone ssh://git.meles.eu/meles2/melesICUmover.git repo-clean cd repo-clean # .env komplett aus der History entfernen git filter-repo --path .env --invert-paths # Secret-Werte auch aus historischen config.yaml-Versionen tilgen. # Die vier ALTEN Werte NICHT hier ins Repo schreiben - die Datei liegt ausserhalb # des Repos und wird nach dem Lauf geloescht. Werte aus der alten .env / History # (git show 400f8a5:config.yaml) entnehmen: cat > ../replacements.txt <<'EOF' ==>ENTFERNT ==>ENTFERNT ==>ENTFERNT ==>ENTFERNT EOF git filter-repo --replace-text ../replacements.txt # Remote neu setzen und History ueberschreiben git remote add origin ssh://git.meles.eu/meles2/melesICUmover.git git push --force --all origin git push --force --tags origin ``` Danach `replacements.txt` loeschen. Alternative: BFG Repo-Cleaner. --- ## Rollback Falls der neue Server Probleme macht, **bevor** Phase 4/6 abgeschlossen sind: alten S3-Key noch aktiv lassen, altes PG-Passwort noch nicht wegwerfen, und den Altserver-Poller wieder starten (`docker compose up -d`). Deshalb Altzugaenge erst in Phase 4 widerrufen. ## Abschluss-Checkliste - [ ] Neuer Server verarbeitet Bilder (`result=success`, S3 + DB aktualisiert) - [ ] Neue S3-Keys aktiv, alte geloescht - [ ] Neues PG-Passwort/-Rolle aktiv, `sslmode=require`, DB-Port nicht oeffentlich - [ ] DeerMapper-Key rotiert (falls genutzt) - [ ] Altserver-Poller gestoppt, `.env` sicher geloescht, Server neu aufgesetzt - [ ] Alte SSH-/Deploy-Keys am Git-Server widerrufen - [ ] Git-History bereinigt + force-push, Team informiert - [ ] Repo enthaelt keine Secrets mehr (`git grep` auf HEAD + Stichprobe History)