Files
Deploymentcenter/docs/UPGRADE.md
T
Deploymentcenter BotandClaude Opus 5 e7fbc85db4 fix(security, core): Auth-Pflicht für Ingest-APIs, 500er-Ursachen beheben, Agenten-Workflow
Sicherheit
- install_db.php war ohne Authentifizierung erreichbar und setzte bei jedem
  Aufruf das Admin-Passwort auf einen fest im Code stehenden Wert zurück.
  Jetzt Auth-Pflicht; ein Konto wird nur bei leerer Benutzertabelle angelegt.
- Stored XSS im Bugtracker-Detail-Modal: Titel, Beschreibung, Fehlermeldung,
  Stacktrace und Kommentare gingen ungefiltert durch innerHTML.
- report.php, projects.php und das Veröffentlichen von Releases verlangen jetzt
  zwingend ein Token. Publish war zuvor völlig ungeschützt.
- CSRF-Token in allen Formularen, Session-Regenerierung nach Login,
  Drosselung fehlgeschlagener Anmeldeversuche.
- Zugangsdaten aus der Versionskontrolle entfernt (Serverdaten.txt,
  config.php, .htpasswd, deploy_config.json). Historie enthält sie weiterhin,
  Rotation erforderlich (siehe docs/UPGRADE.md).
- Token-Validierung nur noch über SHA-256-Hash; expires_at wird ausgewertet.

Behobene 500er
- Audit::log() war in index.php weder eingebunden noch importiert. Jeder
  Klick auf "Aktivierung freigeben" endete in einem Fatal Error.
- Derselbe benannte PDO-Platzhalter mehrfach je Statement (:id in
  revokeToken/deleteToken, :q siebenfach in der Volltextsuche). Bei
  EMULATE_PREPARES=false ist das nicht zulässig und warf HY093.
- Migration 005 nutzte dynamisches SQL, dessen Semikolons in String-Literalen
  vom alten explode(';')-Installer als Statement-Ende gelesen wurden. Sie
  schlug still fehl, wodurch push_id/target_agent/tags dauerhaft fehlten.
- Monitor-Umbenennung ohne Transaktion, verschachtelte Transaktionen im
  RateLimiter.

Funktionale Korrekturen
- Der Watchdog-Evaluator fehlte vollständig: Monitor-Zustände änderten sich nur
  beim Eintreffen eines Heartbeats, ein ausgefallenes System blieb dauerhaft
  "up". Erster Lauf auf dem Produktivsystem: 7 von 10 Monitoren waren
  tatsächlich seit über einem Tag nicht erreichbar.
- Das Feld "os" fehlte im Monitor-Dialog, wurde aber gespeichert und löschte
  damit bei jedem Speichern das Betriebssystem.
- Der Resolve-Dialog existierte im HTML nicht; der Button war funktionslos.
- Versionsvergleich erfolgte lexikografisch, wodurch 1.9.0 als neuer galt
  als 1.10.0.
- Schreiboperationen meldeten Erfolg auch für nicht existierende IDs.
- Post/Redirect/Get gegen doppelte Einträge beim Neuladen.

Neue Struktur
- src/bootstrap.php mit PSR-4-Autoloader ersetzt die require-Ketten.
- Core: Config, Http, Csrf, ApiAuth, Logger, Migrator, ErrorReporter.
- Migrator mit zeichenweisem SQL-Parser, dc_migrations und Baseline-Verfahren,
  damit bestehende Installationen keine Beispieldaten zurückbekommen.

Agenten-Workflow
- Claim/Lease: Items werden exklusiv übernommen, damit nicht zwei Agenten am
  selben Problem arbeiten. action=next holt und reserviert in einem Zug.
- Idempotenz über client_ref, Deduplizierung auch für Feature Requests,
  Erkennung von Regressionen, automatische Eskalation des Schweregrads.
- Strukturierter Code-Kontext (repo_url, commit_sha, file_path, line_no).
- Delta-Abfragen über updated_since, Pagination, Bulk-Update.
- Beim Veröffentlichen eines Releases schließen sich Items mit passendem
  resolved_in_build selbst.
- Ausgehende Webhooks mit HMAC-Signatur, /api/health, /api/openapi.json.
- Unbehandelte Fehler meldet die Plattform in ihren eigenen Bugtracker.

WebUI
- Serverseitige Filterung mit Pagination statt Rendern aller Datensätze.
- Migrations-Schranke, Evaluator-Warnung, Übersicht aktiver Agenten.

Zeitstempel liegen in der Datenbank durchgängig in UTC und werden für die
Anzeige in die App-Zeitzone umgerechnet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:17:36 +02:00

197 lines
7.1 KiB
Markdown

# Umstellung auf Version 2.0 — Ablaufplan
Diese Fassung enthält Sicherheitskorrekturen, die das Verhalten der
Schnittstellen ändern. Bitte in dieser Reihenfolge vorgehen.
---
## 1. Vor dem Deployment: Zugangsdaten wechseln
`Serverdaten.txt`, `config/config.php`, `config/.htpasswd` und
`scripts/deploy_config.json` lagen im Git-Repository. Sie sind jetzt per
`.gitignore` ausgeschlossen und aus dem Index entfernt — **die Git-Historie
enthält sie aber weiterhin**. Alle betroffenen Zugangsdaten sind daher als
kompromittiert zu behandeln:
- [ ] MySQL-Passwort ändern, danach in `config/config.php` eintragen
- [ ] FTP-Passwort ändern, danach in `scripts/deploy_config.json` eintragen
- [ ] `.htpasswd`-Passwort für `deploy` neu setzen
- [ ] Git-Token `eb429575…` widerrufen und neu ausstellen
- [ ] Admin-Passwort im WebUI ändern (die alte Fassung setzte es bei jedem
Aufruf von `install_db.php` auf `Admin1337!` zurück — jeder im Internet
konnte das auslösen)
Wenn die Historie bereinigt werden soll, geht das mit
[`git filter-repo`](https://github.com/newren/git-filter-repo). Das schreibt
alle Commit-Hashes um; bei einem Repository mit mehreren Nutzern vorher abstimmen.
---
## 2. Konfiguration ergänzen
`config/config.php` braucht drei neue Schlüssel unter `security`. Die
mitgelieferte Datei enthält bereits erzeugte Werte; für eine neue Installation:
```bash
cp config/config.example.php config/config.php
openssl rand -hex 32 # je einmal für shared_key, webhook_key, license_key
```
| Schlüssel | Zweck |
|---|---|
| `security.shared_key` | Server-zu-Server-Aufrufe: Evaluator-Cron, Migration, Deaktivierung |
| `security.webhook_key` | HMAC-Signatur ausgehender Webhooks |
| `security.license_key` | Signatur der Offline-Lizenzdateien (`.lic`) |
| `app.debug` | Auf Produktivsystemen `false` — steuert, ob Exception-Texte ausgeliefert werden |
> Der bisherige `shared_key` (`DC_MASTER_SECURE_TOKEN_2026_x98f`) stand im
> Repository und wurde ersetzt. Wer ihn irgendwo eingetragen hat — etwa für
> `/api/license/v1/deactivate` — muss den neuen Wert nachziehen.
---
## 3. Deployment
```bash
python scripts/deploy.py
```
Das Skript überträgt unter anderem die neuen Verzeichnisse `var/` (Logs) und
die zusätzlichen `.htaccess`-Dateien in `config/`, `src/` und `sql/`.
---
## 4. Migration ausführen
Im WebUI anmelden, dann **System → DB-Migration → Migration jetzt ausführen**.
Alternativ über die Kommandozeile:
```bash
php public/install_db.php
```
Oder mit dem Shared Key:
```bash
curl -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/install_db.php
```
Die Migration ist additiv und legt an bzw. korrigiert:
- `dc_migrations` — vermerkt angewendete Versionen, damit nichts doppelt läuft
- `dc_login_attempts` — Drosselung fehlgeschlagener Anmeldungen
- `dc_webhooks` — ausgehende Benachrichtigungen
- Bugtracker: Claim/Lease, `client_ref`, `dedup_key`, Code-Kontextfelder, Indizes
- Watchdog: `last_state_change_utc`, `down_since_utc`, Zustand `unknown`
- **Reparatur von Migration 005** — deren Spalten (`push_id`, `target_agent`,
`tags`) fehlten bisher auf Datenbanken, die aus Migration 004 stammen. Die
alte Fassung nutzte dynamisches SQL, dessen Semikolons in String-Literalen
vom damaligen Installer als Statement-Ende gelesen wurden; die Fehler wurden
stillschweigend verschluckt.
- **Reparatur der Token-Hashes** — die Validierung vergleicht jetzt nur noch
den SHA-256-Hash. Die geseedeten Beispiel-Tokens trugen Hashes, die nicht zu
ihrem Klartext passten; sie werden korrigiert, damit bestehende Tokens
weiterhin funktionieren.
---
## 5. Cron für den Watchdog-Evaluator einrichten
**Ohne diesen Schritt sind die Monitor-Zustände wertlos.** Der Evaluator fehlte
bisher vollständig — der Zustand änderte sich nur beim Eintreffen eines
Heartbeats, ein ausgefallener Server blieb dauerhaft grün.
```bash
* * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
```
Solange der Job fehlt, zeigt das WebUI oben einen Warnhinweis mit einer
Schaltfläche für einen einmaligen Lauf.
---
## 6. Agenten-Tokens ausstellen
Die Ingest-Endpunkte verlangen jetzt zwingend ein Token.
1. WebUI → **Token-Verwaltung → Master-Token erstellen**
2. Scopes wählen (für einen Coding-Agenten: `bugtracker:report`,
`bugtracker:read`, `bugtracker:manage`)
3. Master-Token einmalig kopieren und auf dem Agenten-Rechner als `DC_TOKEN`
hinterlegen — oder den Agenten per `/api/tokens/v1/provision` ein eigenes
Sub-Token ziehen lassen
---
## 7. Bestehende Integrationen anpassen
| Betroffen | Was zu tun ist |
|---|---|
| Aufrufe von `/api/bugtracker/v1/report` ohne Token | Token-Header ergänzen |
| Aufrufe von `/api/bugtracker/v1/projects` ohne Token | Token-Header ergänzen |
| Skripte, die Releases veröffentlichen | Token mit `updateservice:publish` ergänzen |
| Auswertung der Antworten | Neues Format: `{"status":"success",…}` bzw. `{"status":"error","error":{"code":…}}` |
| Watchdog-Agenten mit `wd_live_…`-Token | Laufen unverändert weiter |
| Clients, die `/api/updateservice/v1/check` aufrufen | Unverändert, weiterhin ohne Token |
| Clients, die `/api/license/v1/validate` aufrufen | Unverändert, weiterhin ohne Token |
---
## 8. Zeitzonen
Datenbankzeitstempel liegen jetzt durchgängig in **UTC**; das WebUI rechnet für
die Anzeige in `app.timezone` (Europe/Berlin) um. Vorhandene Datensätze wurden
in Serverzeit geschrieben und erscheinen daher einmalig um den Zeitzonenversatz
verschoben. Für Monitoring-Daten ist das ohne Bedeutung, für den Audit-Log
gegebenenfalls beachten.
---
## 9. Prüfen, ob alles läuft
```bash
curl https://dc.mhdf.de/api/health -H "Authorization: Bearer <SHARED_KEY>"
```
Erwartet wird `"healthy": true`, eine leere `schema.pending`-Liste und ein
`checks.evaluator.ok` von `true`.
Zusätzlich stichprobenartig im WebUI prüfen:
- [ ] Anmeldung funktioniert
- [ ] Projekt anlegen und wieder löschen
- [ ] Monitor bearbeiten — das Feld **Betriebssystem** bleibt nach dem
Speichern erhalten (wurde zuvor bei jedem Speichern geleert)
- [ ] Bugtracker: „🔍 Details" öffnet den Dialog, „✔" öffnet den
Lösen-Dialog (dessen HTML fehlte bisher komplett)
- [ ] Token widerrufen und löschen (warf zuvor `HY093`)
- [ ] Lizenz-Aktivierung freigeben (warf zuvor `Class "Audit" not found`)
- [ ] Nach dem Speichern F5 drücken — es entsteht kein zweiter Eintrag mehr
---
## 10. Optional: Webhooks
Ereignisgesteuerte Benachrichtigung statt Polling. Ziel direkt in der Datenbank
eintragen:
```sql
INSERT INTO dc_webhooks (name, url, project_slug, events, secret, enabled)
VALUES ('Telegram Alarm', 'https://n8n.example.com/webhook/dc',
NULL, 'bug.critical,monitor.down', 'geheimnis', 1);
```
Verfügbare Ereignisse: `bug.created`, `bug.critical`, `bug.resolved`,
`feature.created`, `monitor.down`, `monitor.recovered`, `release.published`,
oder `*` für alle.
Jede Zustellung trägt eine Signatur:
```
X-DC-Timestamp: 1754563200
X-DC-Signature: sha256=<hex(hmac_sha256(secret, timestamp + "." + body))>
```
Nach 20 Fehlversuchen in Folge deaktiviert sich ein Webhook selbst.