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

7.1 KiB

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. 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:

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

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:

php public/install_db.php

Oder mit dem Shared Key:

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.

* * * * * 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

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:

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.