morz-infoboard/docs/WATCHDOG-KONZEPT.md
Jesko Anschütz dd3ec070f7 Security-Review + Phase 6: CSRF, Rate-Limiting, Tenant-Isolation, Screenshot, Ansible
### Security-Fixes (K1–K6, W1–W4, W7, N1, N5–N6, V1, V5–V7)
- K1: CSRF-Schutz via Double-Submit-Cookie (httpapi/csrf.go + csrf_helpers.go)
- K2: requireScreenAccess() in allen manage-Handlern (Tenant-Isolation)
- K3: Tenant-Check bei DELETE /api/v1/media/{id}
- K4: requirePlaylistAccess() + GetByItemID() für JSON-API Playlist-Routen
- K5: Admin-Passwort nur noch als [gesetzt] geloggt
- K6: POST /api/v1/screens/register mit Pre-Shared-Secret (MORZ_INFOBOARD_REGISTER_SECRET)
- W1: Race Condition bei order_index behoben (atomare Subquery in AddItem)
- W2: Graceful Shutdown mit 15s Timeout auf SIGTERM/SIGINT
- W3: http.MaxBytesReader (512 MB) in allen Upload-Handlern
- W4: err.Error() nicht mehr an den Client
- W7: Template-Execution via bytes.Buffer (kein partial write bei Fehler)
- N1: Rate-Limiting auf /login (5 Versuche/Minute pro IP, httpapi/ratelimit.go)
- N5: Directory-Listing auf /uploads/ deaktiviert (neuteredFileSystem)
- N6: Uploads nach Tenant getrennt (uploads/{tenantSlug}/)
- V1: Upload-Logik konsolidiert in internal/fileutil/fileutil.go
- V5: Cookie-Name als Konstante reqcontext.SessionCookieName
- V6: Strukturiertes Logging mit log/slog + JSON-Handler
- V7: DB-Pool wird im Graceful-Shutdown geschlossen

### Phase 6: Screenshot-Erzeugung
- player/agent/internal/screenshot/screenshot.go erstellt
- Integration in app.go mit MORZ_INFOBOARD_SCREENSHOT_EVERY Config

### UX: PDF.js Integration
- pdf.min.js + pdf.worker.min.js als lokale Assets eingebettet
- Automatisches Seitendurchblättern im Player

### Ansible: Neue Rollen
- signage_base, signage_server, signage_provision erstellt
- inventory.yml und site.yml erweitert

### Konzept-Docs
- GRUPPEN-KONZEPT.md, KAMPAGNEN-AKTIVIERUNG.md, MONITORING-KONZEPT.md
- PROVISION-KONZEPT.md, TEMPLATE-EDITOR.md, WATCHDOG-KONZEPT.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:06:35 +01:00

8.4 KiB

Info-Board Neu - Watchdog-Konzept

Ziel

Der Watchdog ueberwacht die kritischen Komponenten des Players und sorgt dafuer, dass der Display-Betrieb bei Abstuerzen oder Verhaengungen automatisch wiederhergestellt wird.

Die Ueberwachung erfolgt auf zwei Ebenen:

  1. Browser-Watchdog — Ueberwachung von Chromium
  2. Agent-Watchdog — Ueberwachung des Player-Agents

Grundprinzipien

  • Watchdogs sind extern und unabhaengig von den ueberwachten Prozessen
  • Erkennung erfolgt aktiv durch Health-Checks, nicht durch Liveness-Pings
  • Restart-Strategien sind progressiv und vermeiden Restart-Schleifen
  • Logging ist strukturiert und fuer Admin-Diagnosen aussagekraeftig

Browser-Watchdog (Chromium-Ueberwachung)

Aufgaben

Der Browser-Watchdog sorgt dafuer, dass:

  • Chromium staendig laeuft und antwortet
  • der Renderer nicht in einer Endlosschleife haengt
  • Rendering-Fehler nicht zu permanenten Schwarzbildern fuehren
  • bei Chromium-Crash oder Verhaengung schnell neugestartet wird

Health-Check-Verfahren

Der Watchdog fuehrt regelmaeßig folgende Checks durch:

1. Prozess-Check

Existiert der Chromium-Prozess noch?
  - lsof oder ps-Abfrage auf die PID
  - Timeout: sofort bei fehlender PID

2. HTTP-Health-Check auf localhost

GET http://localhost:8081/health
Timeout: 5 Sekunden
Erwartet: 200 OK und JSON-Antwort {status: "ok"}

Die player-ui muss einen einfachen /health-Endpunkt bereitstellen, der schnell antwortet, auch wenn die Playlist gerade verarbeitet wird.

3. Rendering-Verifizierung (optional, Phase 2)

Screenshot-basiert erkennen, ob der Browser:
  - Fehlerseite zeigt
  - komplett schwarz ist (mehr als 95% schwarze Pixel)
  - seit mehreren Minuten denselben Content zeigt, obwohl ein Wechsel erwartet wurde

Diese Methode ist fuer v1 optional, wird aber fuer spaetere Verhaengungserkennung eingeplant.

Ueberwachungs-Intervall

  • Health-Check alle 30 Sekunden
  • Bei Fehler: sofort Neustart pruefen (kein Warten auf naechsten Zyklus)

Restart-Strategie

Strategie: Exponentieller Backoff mit Maximum

Fehlerfall:
  Fehler 1: Sofort neustart (Wait 0s)
  Fehler 2: Warte 2s, versuche Restart
  Fehler 3: Warte 5s, versuche Restart
  Fehler 4: Warte 10s, versuche Restart
  Fehler 5+: Warte 30s, versuche Restart
  Nach 10 aufeinanderfolgende Fehler ohne erfolgreicher Recovery:
    - Alert an Admin (via Server-Status)
    - Overlay auf "Error" setzen
    - Watchdog-Loop verlangsamen auf 5 Min Intervall

Erfolg-Kriterium

Wenn der Health-Check 3x hintereinander erfolgreich ist:

  • Backoff-Zaehler zuruecksetzen auf 0
  • naechstes Fehler wieder mit sofort-Restart starten

Logging

Jeder Watchdog-Ereignis wird protokolliert:

{
  "ts": "2025-03-23T14:22:15Z",
  "component": "browser_watchdog",
  "event": "restart",
  "reason": "health_check_timeout",
  "attempt": 2,
  "next_retry_in_ms": 5000,
  "details": {
    "pid_before": 1234,
    "pid_after": 1245,
    "http_status_before": 0
  }
}

Logging-Ziele:

  • strukturiert auf stdout/stderr (JSON)
  • lokal in /var/log/signage/watchdog.log mit Rotation

Agent-Watchdog (systemd-Integration)

Aufgaben

Der Agent-Watchdog (bzw. systemd-Unit) sorgt dafuer, dass:

  • der Player-Agent staendig laeuft
  • nach Crash oder gewolltem Stop schnell neugestartet wird
  • Restart-Grenzen ein Verhaengungsloop verhindern

systemd-Konfiguration

[Service]
Type=simple
ExecStart=/usr/local/bin/player-agent
Restart=always
RestartSec=5
StartLimitInterval=300
StartLimitBurst=10
StandardOutput=journal
StandardError=journal

Bedeutung:

  • Restart=always — Neustart bei jedem Exit (unabhaengig vom Exit-Code)
  • RestartSec=5 — Warte 5 Sekunden vor Neustart
  • StartLimitInterval=300 — Zaehle Restarts in einem 300s-Fenster
  • StartLimitBurst=10 — Mehr als 10 Restarts in 300s fuehrt zu systemd-Stop

Wenn StartLimitBurst erreicht wird:

  • systemd laesst den Service stehen
  • Admin wird informiert (Status-API setzt agent_watchdog_failed)
  • manueller Eingriff oder Admin-Kommando noetig

Health-Check durch Agent selbst

Der Agent sollte intern:

  • Broker-Verbindung regelmaeßig pruefen
  • Server-Sync-Status tracken
  • bei kritischen Innenfehlern nicht einfach weiterlaeufen

Wenn sich der Agent selbst als unheilbar beschaedigt sieht:

  • strukturiert mit Exit-Code 1 beenden (systemd startet neu)
  • nicht mit exit(0) haengend beenden

Verhaeltnis zu systemd

Architektur-Entscheidung

systemd uebernimmt die Prozess-Wiederbelebung fuer den Agent.

Der Browser-Watchdog ist ein separater, von systemd unabhaengiger Prozess, weil:

  • Chromium staendiger Ueberwachung bedarf (Health-Checks im 30s-Rhythmus)
  • ein Systemd-Watchdog-Timer zu unverzeihlich waere (nur on/off, nicht granular)
  • der Browser-Watchdog auch die Systemd-Unit selbst monitoren kann (Defensive Architektur)

Optional: systemd WatchdogSec

Fuer den Agent ist es sinnvoll, auch systemd's Watchdog-Timer zu nutzen:

[Service]
WatchdogSec=30
ExecStart=/usr/local/bin/player-agent

Der Agent muesste dann periodisch systemd-notify --ready senden.

Das ist optional fuer v1, wird aber fuer spaetere Robustheit eingeplant.

Integration mit Player-Setup

Verzeichnisstruktur

/usr/local/bin/
  player-agent           — Go-Binary
  browser-watchdog       — Go-Binary oder Shell-Script

/etc/systemd/system/
  signage-agent.service
  signage-browser-watchdog.service

/var/lib/signage/
  watchdog-state.json   — letzter Zustand, Backoff-Counter

/var/log/signage/
  watchdog.log          — strukturiertes Logging

Startup-Reihenfolge

  1. Basis-System bootet, X11 startet
  2. signage-agent.service startet (systemd)
  3. Agent startet, prueft Konfiguration, startet player-ui HTTP-Server
  4. signage-browser-watchdog.service startet (systemd)
  5. Watchdog wartet initial 10s, bevor erste Checks starten
  6. Agent laesst Chromium starten
  7. Watchdog beginnt Health-Checks

Dieses Ordering verhindert, dass der Watchdog versucht, den Browser zu uberwachen, bevor der Agent bereit ist.

Stopp-Reihenfolge bei Shutdown

  1. systemd sendet SIGTERM an Agent und Browser-Watchdog
  2. Watchdog: beendet sich, versucht nicht zu restarten
  3. Agent: beendet sich, laedt Chromium herunter
  4. Systemd wartet auf Completion

Fehlerklassifizierung und Admin-Reporting

Fehlerklassen

Fehlerklasse Symptom Watchdog-Aktion Admin-Alert
Prozess-Crash PID weg Sofort neustart Nach 3x Fehlschlag
Health-Check-Timeout HTTP timeout Backoff-Restart Nach 5x Fehlschlag
Rendering-Fehler Browser zeigt Fehlerseite Neustart Sofort sichtbar
Backoff-Maximum 10+ Fehler in 5min Stoppen, Alert Sofort
Agent-Unhealthy Server-Sync fehlgeschlagen Systemd-Neustart Nach 3x Sync-Fehler

Admin-Oberflaeche

Status-Page und Admin-Dashboard zeigen:

{
  "screen_id": "info01",
  "browser_status": {
    "pid": 1234,
    "health": "ok",
    "last_check_at": "2025-03-23T14:25:00Z",
    "restart_count_5m": 0,
    "last_error": null
  },
  "agent_status": {
    "pid": 567,
    "uptime_seconds": 3600,
    "sync_status": "ok",
    "last_sync_at": "2025-03-23T14:24:55Z",
    "systemd_restart_count": 0
  },
  "watchdog_alert": null
}

Konfigurierbare Parameter

In /etc/signage/config.yml oder Umgebungsvariablen:

watchdog:
  browser:
    check_interval_sec: 30
    health_check_timeout_sec: 5
    restart_backoff_steps: [0, 2, 5, 10, 30]  # Sekunden
    max_consecutive_errors: 10
    error_window_sec: 300
  agent:
    systemd_unit: "signage-agent.service"
    healthcheck_timeout_sec: 10

Testing und Validierung

Testfaelle fuer den Watchdog:

  1. Chromium manuell toeten (kill -9 PID) — sollte innerhalb 30s neustartet werden
  2. Player-Agent starten/stoppen — systemd sollte neustart triggern
  3. Player-UI HTTP-Server abschalten — Browser-Watchdog sollte neustarten
  4. Schnelle aufeinanderfolgende Crashes — Backoff-Exponentialfunktion pruefen
  5. Admin-Kommando restart_player — geordneter Neustart, dann Restart-Counter nicht erhoeht
  6. Watchdog-Logs auf Struktur und Vollstaendigkeit pruefen

Zusammenfassung

Der Watchdog-Ansatz ist:

  • Transparent — klare Logging und Admin-Sichtbarkeit
  • Progressive — Backoff statt Restart-Schleife
  • Defensiv — mehrere Erkennungsmethoden (Prozess, HTTP, optional Rendering)
  • Integriert — arbeitet mit systemd zusammen, nicht gegen es
  • Skalierbar — Verfahren gilt fuer alle Player unabhaengig von Standort oder Netzwerk