Logbuch#homeos#security#monitoring#selfhosting
Session-Login im Backend statt Reverse-Proxy — und eine Baseline, die stille Annahmen entlarvt
Von Niclas Hennrich · veröffentlicht · geschehen am
Alternativen bewertet und einen Weg festgelegt. Gehört zum Vorhaben HomeOS — Control Center mit drei Säulen.
- API-Latenz P95, langsamster Endpunkt
- 22ms
- HomeOS-Stack idle
- 676MB RAM
- Zeitreihen in VictoriaMetrics
- 11328
HomeOS ist mein selbst gebautes Control Center fürs Homelab — aus dem Infrastruktur-NOC gewachsen, heute mit drei Säulen: Infrastruktur, Haus (Smart Home) und Family (Haushalt). Nach Wochen im Ausbau war der Tag für etwas anderes da: nichts Neues bauen, sondern den Ist-Stand verifizieren statt dem letzten Report zu glauben, die Anmeldung fertig machen und den Zustand als Baseline einfrieren.
Die Entscheidung: Mit inzwischen rund 100 schreibenden API-Pfaden
wollte ich Schreibrechte nicht mehr allein an der Netzgrenze festmachen,
sondern an Konten und Rollen. Drei Wege standen zur Wahl:
ein Reverse-Proxy mit vorgeschalteter Anmeldung, ein statischer Token im
Client oder ein Session-Login im Backend selbst. Der Proxy hätte die
Anwendung nichts über Rollen wissen lassen — „Familie darf Essen planen,
aber keine Firewall-Regel anfassen" ist im Proxy nicht abbildbar. Ein
statischer Token ist ein Passwort, das nie abläuft und in jedem Client
liegt. Also Session-Login: zwei Tabellen (Konten mit bcrypt-Hash, Sessions
nur als SHA-256), zwei Rollen (owner und member), ein
Login-Rate-Limit und ein Origin-Check.
Der Kern ist eine einzige Middleware. Sie greift über alle
mutierenden Pfade unter /api/v1: Member dürfen in Familie und Haus
schreiben, alles andere braucht Owner, und ein neuer Pfad ist automatisch
streng, bis jemand ihn ausdrücklich lockert. 100 Routen einzeln zu schützen
hieße, die 101. zu vergessen. Methode plus Präfix an einer Stelle macht
„vergessen" strukturell unmöglich. Lesepfade bleiben bewusst offen — der
NOC-Bildschirm, die Wandanzeige und die Metrik-Scrapes sollen nicht an einer
Session hängen.
Damit die Migration niemanden aussperrt, kennt das Backend einen Übergangsmodus: Solange kein Konto existiert, läuft es wie zuvor und zeigt das als Alert in der Übersicht; das erste Owner-Konto schaltet scharf. Das zahlte sich doppelt aus: Der Testzustand (keine Konten) ist exakt dieser Übergangsmodus, deshalb blieben alle 1281 Bestandstests unverändert grün, und 15 neue Regressionstests decken die Rollenmatrix ab. Ein Smoke-Test danach: 15 Kernrouten mit 200, ein alter Client-Durchlass sauber als 422 belegt, fremder Origin mit 403 abgewiesen. Der Fernseher (webOS-Client) fand nach dem Backend-Neustart von selbst zurück.
„Umgesetzt" und „wirksam" sind zwei Messungen. Das ist die Lehre, die ich mitnehme: Eine Anmeldung ist erst dann Schutz, wenn das erste Konto angelegt ist. Meine Security-Matrix unterscheidet das deshalb explizit — gebaut und getestet ist ein anderer Zustand als aktiv, und der Alert macht den Unterschied zum sichtbaren Zustand statt zum Geheimnis. Ein Audit der Rechtevergabe auf dem Host lieferte in dieselbe Liste noch einen Nebenbefund — eine Bequemlichkeit aus der Aufbauphase, die ein Audit sichtbar macht und ein Report nie erwähnt hätte.
Die Baseline: 40 Requests je Endpunkt, Perzentile aus der sortierten Liste; Schreibrate der Datenbank zweimal im 30-Sekunden-Abstand; Serien und Größe des Zeitreihen-Speichers aus dessen Status-Endpunkt. Ergebnis, mit Git-Tag eingefroren: API P50 zwischen 7 und 19 ms, P95 höchstens 22 ms; der HomeOS-Stack im Leerlauf bei etwa 2,3 % CPU und 676 MB RAM; Postgres 23 MB mit 148 Writes pro Minute auf fünf Verbindungen; VictoriaMetrics 11 328 Serien bei rund 216 MB; 32 von 33 Scrape-Zielen erreichbar, das fehlende ein bekannter und dokumentierter Fall. 1298 Tests grün.
Was die Messung nebenbei fand: Der Status-Endpunkt von VictoriaMetrics
liefert die Serienzahl unter totalSeries, nicht wie Prometheus unter
headStats.numSeries — mein erster Parser gab still None zurück. Und die
Abfrage zum Backup-Alter war ohne last_over_time leer, weil der letzte
Wert als veraltet galt. Beim Nachbohren stellte sich heraus: Nur das
tägliche Datenbank-Backup meldet überhaupt einen Erfolg, der wöchentliche
Snapshot des Zeitreihen-Speichers nicht. Meine „vollständige"
Backup-Überwachung hatte die Hälfte nie gesehen. Sichtbar wurde das nicht
durch einen Test, sondern durch den Versuch, den Wert zu messen. Genau dafür
ist eine Baseline da.