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

305 lines
8.4 KiB
Markdown

# 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:
```json
{
"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
```ini
[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:
```ini
[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:
```json
{
"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:
```yaml
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