Zum Inhalt springen
labkompass

Logbuch#homeos#security#audit#docker#smart-home

Prod lief auf Mock-Geräten

Von Niclas Hennrich · veröffentlicht · geschehen am


ProblemBehoben

Etwas ist ausgefallen oder kaputtgegangen. Gehört zum Vorhaben HomeOS — Control Center mit drei Säulen.

Gelöschte Mock-Ereignisse
1160
Echte Shellys in Prod (vorher nur Mock)
5
Empfehlungen warm (vorher 442 ms)
17ms

HomeOS ist mein selbst gebautes Control Center fürs Homelab — aus dem Infrastruktur-NOC erwachsen, mit drei Säulen: Infrastruktur, Haus (Smart Home) und Family/Haushalt. Die Haus-Säule spricht mit ein paar Shellys, kleinen WLAN-Schaltaktoren mit Leistungsmessung.

Was ich wollte: einen zweiten Audit-Lauf, diesmal mit Security-Schwerpunkt — Auth-Flächen, API-Eingaben, Secrets, Dependencies, Docker-Hardening, die IoT-Integrationen — plus eine Effizienz-Runde über den Food-Code der Tage zuvor. Vier parallele, rein lesende Reviews, dazu eine Live-Baseline auf optimus: Latenzen per curl, Datenbank-Schreiblast über 60 Sekunden, docker stats. Rund 35 Findings, 28 davon direkt behoben.

Der größte Fund kam aus keinem Review. Er kam aus einer Logzeile der Baseline:

Poller für mock gestartet

Die Haus-Säule lief in Produktion seit dem Deploy am Vortag komplett auf dem Mock-Adapter. Alle Geräte, die Energie-Seite, sämtliche Zeitreihen: erfundene Daten. Die echten Shellys hatte ich validiert — aber nur aus der Dev-Umgebung heraus. In Prod fehlten die Werte in der .env, und der Compose-Default sprang ein: mock.

Die Ursache ist unbequem, weil sie dokumentiert war. Der Handgriff stand in der STATUS-Datei und in der Beispiel-Env: „Ohne diese Werte fällt das Modul auf den Mock-Adapter zurück." Beim Deploy hat ihn niemand ausgeführt, und nichts hat ihn erzwungen. Für eine andere Variable hatte ich das längst: Compose bricht mit ${VAR:?} den Start ab, wenn sie fehlt. Für den Adapter nicht. Dieselbe Fehlerklasse wie beim Mealie-Token und bei der ARP-Tabelle: Ein Kommentar ist kein Zustand.

Die Bereinigung: Vorab-Dump der Datenbank, dann Adapter auf shelly und die Adressen der fünf Geräte in die Prod-Env, Mock-Integration samt Geräten und Zuständen kaskadiert gelöscht, 1160 Mock-Ereignisse entfernt, die HomeOS-Serien im Zeitreihen-Speicher gelöscht, damit der nächste Scrape nur noch echte schreibt. Ergebnis: 5/5 Shellys online, reale Leistungswerte (in Summe rund 444 W) in Datenbank und Zeitreihen. Prod zeigt zum ersten Mal echte Hausdaten.

Was sonst passiert ist, im Schnelldurchlauf: Korrektheits-Fixes im Food-Code (eine Division durch null bei 100 % Rabatt, Eingabegrenzen, ein Größenlimit beim ICS-Abruf), Datenbank-Härtung mit Dedupe-Index und fehlenden FK-Indizes, Container-Hardening, ein Lockfile für die Python-Dependencies, ein Empfehlungs-Cache. Der brachte die Empfehlungen von 442 ms je Poll auf 16–18 ms warm — der Kaltstart kostet mit 651 ms allerdings mehr als vorher. Und die Datenbank-Schreiblast stieg von 71 auf 106 Commits pro Minute. Das ist keine Regression, sondern ehrlich benannt: Prod tut jetzt echte Arbeit, 28 statt 10 Entitäten im Poll. Tests von 1207 auf 1223.

Was ich mitnehme:

  • Eine Baseline-Messung schlägt vier Code-Reviews. Der kritischste Befund stand in keinem File-Diff. Er fiel auf, weil eine Logzeile nicht zur dokumentierten Wirklichkeit passte.
  • Ein dokumentierter Handgriff ohne Zwang geht verloren. Was beim Deploy nicht erzwungen wird, passiert nicht. Die Doku war korrekt und hat trotzdem nichts verhindert.
  • Ein Cache braucht vier Antworten: Source of Truth, TTL, Invalidierung, Fallback. Mit einem Versionszähler ist die Invalidierung vollständig, ohne jeden Endpunkt einzeln zu verdrahten.
  • Tests, die den HTTP-Client mocken, zementieren den Client. Der Wechsel von get auf stream kostete drei Tests. Der Mock gehört an die Grenze — den Transport —, nicht an die Funktion.
  • Range-Pins sind keine Reproduzierbarkeit. Erst das Lock aus dem validierten Prod-Stand macht einen Build deterministisch; vorher zog jeder Build rund 40 ungeprüfte transitive Neuerscheinungen.