update.sh meldet nach dem Neustart ein Event (POST /api/v1/events/system, Admin-Key). upsertPullRequest markiert bereits vorhandene Tickets als Pull Request, damit sie den /pulls/-Link bekommen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
104 lines
6.6 KiB
Markdown
104 lines
6.6 KiB
Markdown
# 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](https://codeberg.org/fds2610/Agent) 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`.
|
|
Nach dem Neustart meldet das Skript ein Event "Monitor: Update eingespielt (<Commit>)" per
|
|
`POST /api/v1/events/system` (Admin-Key aus der `.env`); schlaegt das fehl, bleibt das Update gueltig.
|
|
- **`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](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:**
|
|
```bash
|
|
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.
|
|
8. **Fallback bei verpasstem Webhook (automatisch, keine Einrichtung):** Sinkt im
|
|
`sync`-Container die Zahl offener PRs (Merge oder Schliessen), legt er `api/data/deploy-requested`
|
|
an. `deploy-webhook.py` bemerkt die Datei, holt `origin/<Branch>` und startet `update.sh`
|
|
nur, wenn der Stand abweicht. Pfad aenderbar ueber `MONITOR_DEPLOY_TRIGGER_FILE`. Greift
|
|
spaetestens beim naechsten Sync (`MONITOR_SYNC_INTERVAL`), keine zusaetzliche Codeberg-Verbindung.
|
|
Trifft ein Ausloeser waehrend eines laufenden Updates ein, laeuft danach ein Folgelauf.
|
|
|
|
## Testen
|
|
|
|
Zustellung testen (Codeberg schickt beim Anlegen automatisch ein `ping`-Event, sichtbar unter
|
|
"Letzte Zustellungen" im Webhook selbst). Manuell mit korrekter Signatur:
|
|
|
|
```bash
|
|
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.
|