monitor/README_UPDATE.md
fds2610-bot 25f8acacc6 Update bricht nicht mehr an unversionierten Laufzeitdateien ab
Auf dem Produktionsserver scheiterte "update.sh" an der untracked hosts.conf:
seit Issue #70 schreibt check_fds.sh diese Datei bei jedem Lauf neu, versioniert
ist nur hosts-sample.conf - in .gitignore stand sie aber (anders als
monitor-api.conf und credentials.conf) nicht.

- hosts.conf in .gitignore aufgenommen
- update.sh blockiert nur noch bei Aenderungen an versionierten Dateien und
  listet sie auf; unversionierte Dateien werden nur als Hinweis geloggt, denn
  der ff-only-Pull laesst sie unangetastet bzw. bricht selbst kontrolliert ab
- neuer Notausgang UPDATE_ALLOW_DIRTY=1 fuer den Fall, dass doch einmal an einer
  versionierten Datei gedreht wurde

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBVdYcfaXNq49bMdQQcZue
2026-09-01 17:45:42 +02:00

5.8 KiB

Auto-Update auf einem Produktionsserver

Dieses Dokument beschreibt, wie ein bereits laufendes monitor auf einem Produktionsserver sich selbst aktualisiert, sobald der Repo-Besitzer einen Pull Request nach main mergt. Anders als im parallelen Agent-Projekt gibt es hier keine Trennung zwischen einem unveraenderlichen "Bootstrap"-Updater und dem eigentlichen Projekt-Code - update.sh gehoert direkt zum Projekt und wird bei jedem Update mit aktualisiert.

Bausteine

  • update.sh (Projekt-Wurzel) - holt die neueste Version des konfigurierten Branches per git pull --ff-only, baut die Docker-Images neu, startet api, web und sync neu und fuehrt (per Default) die Testsuiten aus. Kann jederzeit auch manuell aufgerufen werden, siehe Kommentar am Kopf der Datei fuer die Umgebungsvariablen UPDATE_REMOTE, UPDATE_BRANCH, UPDATE_RUN_TESTS.
  • deploy-webhook.py (Projekt-Wurzel) - kleiner HTTP-Server (nur Python-Standardbibliothek, keine Abhaengigkeiten) auf Basis von http.server. Prueft eingehende Codeberg-Webhooks per HMAC-Signatur (X-Forgejo-Signature/X-Gitea-Signature, analog zum bestehenden POST /api/v1/webhooks/codeberg aus API.md) und ruft bei einem Push auf den konfigurierten Branch update.sh auf. Laeuft auf dem Host, nicht im Docker-Compose-Stack
    • update.sh selbst ruft docker compose auf dem Host auf, das waere aus einem Container heraus nur mit riskantem Zugriff auf den Docker-Socket moeglich.
  • deploy/monitor-deploy-webhook.service - systemd-Unit-Vorlage, um deploy-webhook.py dauerhaft im Hintergrund laufen zu lassen.

Manuelle Vorarbeiten auf dem Server

Diese Schritte sind einmalig und nicht Teil dieses Repos (unterschiedlich je nach Server), daher hier nur als Checkliste:

  1. Repo auskecken. git clone https://codeberg.org/fds2610/monitor.git (oder per SSH, falls Push-Zugangsdaten fuer git pull als Deploy-Key/SSH-Key eingerichtet werden) nach z.B. /opt/monitor, Branch main ausgecheckt. Versionierte Dateien duerfen dort nicht lokal veraendert werden - update.sh bricht kontrolliert ab, statt die Aenderungen zu ueberschreiben (Notausgang: UPDATE_ALLOW_DIRTY=1). Unversionierte Dateien neben dem Repo (.env, hosts.conf, monitor-api.conf, credentials.conf) stoeren dagegen nicht, sie bleiben beim Update unangetastet.
  2. Docker + Docker Compose installieren (docker compose version muss funktionieren). Der Benutzer, unter dem deploy-webhook.py laeuft, braucht Mitgliedschaft in der docker-Gruppe (oder aequivalente Rechte), um docker compose ohne sudo aufzurufen.
  3. Python 3 installieren (Standardbibliothek genuegt, keine weiteren Pakete noetig) - mail_fds.py im selben Repo setzt das ohnehin schon voraus.
  4. .env anlegen aus .env.example, insbesondere MONITOR_ADMIN_API_KEY, MONITOR_API_BASE und die neuen Variablen MONITOR_DEPLOY_WEBHOOK_SECRET, MONITOR_DEPLOY_WEBHOOK_PORT, MONITOR_DEPLOY_WEBHOOK_BRANCH (Details siehe dort). Das Secret z.B. mit openssl rand -hex 32 erzeugen.
  5. systemd-Unit einrichten:
    sudo cp deploy/monitor-deploy-webhook.service /etc/systemd/system/
    sudo $EDITOR /etc/systemd/system/monitor-deploy-webhook.service   # Pfade/User/Group anpassen
    sudo systemctl daemon-reload
    sudo systemctl enable --now monitor-deploy-webhook
    sudo systemctl status monitor-deploy-webhook
    
  6. Port/Proxy freischalten. deploy-webhook.py lauscht auf MONITOR_DEPLOY_WEBHOOK_PORT (Default 9099) auf allen Interfaces. Analog zum bestehenden Codeberg-Sync-Webhook muss Proxy/Firewall eingehende Anfragen von Codeberg an diesen Port (bzw. an einen dahinter liegenden Reverse-Proxy-Pfad) durchlassen - alle anderen Dienste (api, web, sync) brauchen dafuer keine Aenderung.
  7. Webhook in Codeberg einrichten (macht der Repo-Besitzer manuell, siehe Issue #36): Repo-Einstellungen -> Webhooks -> Forgejo, Ziel-URL auf deploy-webhook.py (Port aus Schritt 6, ggf. per Proxy auf https://... gemappt), Content-Type application/json, Secret wie in .env (MONITOR_DEPLOY_WEBHOOK_SECRET), Ereignis nur "Push". Ein Merge erzeugt immer einen Push auf den Ziel-Branch, ein eigenes "Pull Request merged"-Event ist daher nicht noetig.

Testen

Zustellung testen (Codeberg schickt beim Anlegen automatisch ein ping-Event, sichtbar unter "Letzte Zustellungen" im Webhook selbst). Manuell mit korrekter Signatur:

SECRET=<MONITOR_DEPLOY_WEBHOOK_SECRET aus .env>
BODY='{"ref":"refs/heads/main"}'
SIG=$(python3 -c "import hmac,hashlib,sys; print(hmac.new(sys.argv[1].encode(), sys.argv[2].encode(), hashlib.sha256).hexdigest())" "$SECRET" "$BODY")
curl -i -X POST "http://<server>:9099/" \
  -H "X-Forgejo-Event: push" \
  -H "X-Forgejo-Signature: $SIG" \
  -d "$BODY"

Antwort 202 {"status": "update gestartet"} bedeutet, dass update.sh im Hintergrund laeuft. Der Verlauf landet in MONITOR_DEPLOY_LOG_FILE (Default: deploy-webhook.log neben deploy-webhook.py) sowie im systemd-Journal (journalctl -u monitor-deploy-webhook -f).

Sicherheit

Der Endpunkt ist wie POST /api/v1/webhooks/codeberg ohne Secret grundsaetzlich geschlossen (401) und akzeptiert nur Anfragen mit gueltiger HMAC-Signatur. Er loest im Erfolgsfall lediglich denselben git pull --ff-only + Rebuild aus, den ein Server-Betreiber auch manuell per ./update.sh anstossen wuerde - er bringt also keinen Code auf den Server, der nicht bereits reviewt und manuell nach main gemergt wurde. Diese manuelle Merge-Hürde ist laut Repo-Besitzer (Issue #36) als alleinige Sicherheitsmassnahme ausreichend; zusaetzliche Branch-Protection-Einstellungen im Codeberg-Repo (z.B. kein direkter Push nach main) bleiben trotzdem empfehlenswert, sind aber eine manuelle Repo-Einstellung und nicht Teil dieses Skripts.