Skip to content
TAA
Notiz 3 min

Dead Switch und RTSP-Timeout: Heimserver-Sicherheit von Grund auf

Diese Woche: zwei gezielte Fixes an meinem Kamera-Backup-System — ffmpeg-Timeout, entkoppelter Heartbeat und ein überarbeitetes Flow-Diagramm.

  • app architektur
  • camera-s3-stream
  • docker
  • shell
  • build-in-public
  • security
Teilen

Mittwochabend, kurz vor elf. Ich sitze vor dem Log meines Heimserver-Kamerasystems und sehe, was ich eigentlich nicht sehen wollte: Der Heartbeat läuft weiter, obwohl der rclone-Prozess längst abgestürzt ist. Das System hält sich für lebendig. Es ist es nicht.

Genau dafür ist ein Dead Switch da — und genau dafür hatte meiner einen blinden Fleck.

App-Architektur im Kleinen: Liveness richtig definieren

camera-s3-stream ist ein privates Sicherheitssystem für meinen Heimserver. IP-Kameras streamen per RTSP, ffmpeg schneidet Segmente, rclone schiebt die Fragmente in einen S3-Bucket. Ein separater Heartbeat-Prozess schlägt regelmäßig auf einen Dead-Switch-Endpunkt auf — solange er schlägt, ist alles gut. Hört er auf, gibt es einen Alarm.

Das klingt wasserdicht. War es nicht.

Das Problem war eine implizite Kopplung: Der Heartbeat hing am Exit-Code von rclone. Wenn rclone hängenblieb — nicht abstürzte, sondern einfach aufhörte, Segmente zu übertragen — lief der Heartbeat trotzdem weiter. Der Dead Switch bekam weiter sein Signal. Kein Alarm. Kein Hinweis. Stille.

Das ist die gefährlichste Art von Fehler in einem Überwachungssystem: kein Absturz, kein Error-Log, einfach keine Daten mehr.

Der Fix hat zwei Teile.

Erstens: ffmpeg bekommt jetzt einen expliziten RTSP-Timeout (-timeout 10000000 — zehn Sekunden in Mikrosekunden). Bisher konnte sich ffmpeg bei einer hängenden Kameraverbindung unbegrenzt aufhängen. Jetzt bricht es sauber ab, statt stillschweigend zu blockieren.

Zweitens: Der Heartbeat ist jetzt von rclone entkoppelt. Er prüft nicht mehr, ob rclone erfolgreich war, sondern ob ein frisches Segment tatsächlich im Ziel-Bucket gelandet ist — konkret: ob sich der Timestamp des neuesten Objekts innerhalb eines definierten Fensters bewegt hat. Kein frisches Segment, kein Heartbeat. Kein Heartbeat, Alarm.

Das ist ein konzeptuell anderer Ansatz. Vorher: Prozess-Liveness. Jetzt: Output-Liveness. Der zweite ist strenger und ehrlicher.

Ich muss zugeben, dass mir diese Lücke länger bekannt war als mir lieb ist. Ich hatte sie als “theoretisches Problem” abgehakt. Mittwochnacht habe ich verstanden, warum das keine gute Idee war.

Flow-Diagramm: Dokumentation, die stimmt

Der zweite Commit dieser Woche war Dokumentation — aber nicht die kosmetische Sorte.

Das bestehende Flow-Diagramm zeigte den alten Ablauf: Heartbeat → rclone-Exit. Nach dem Refactoring stimmte es nicht mehr. Ein Diagramm, das den falschen Zustand dokumentiert, ist schlimmer als gar keins; es gibt einem das Gefühl, das System zu verstehen, obwohl man es nicht tut.

Ich habe das Diagramm aktualisiert und dabei die Heartbeat-Semantik explizit eingebaut: welches Signal als “fresh segment liveness” gilt, welches Zeitfenster akzeptabel ist, was passiert wenn das Fenster überschritten wird. Zwei Stunden Arbeit für etwas, das kein Nutzer je sieht. Trotzdem war es die richtige Entscheidung.

Was das mit App-Entwicklung zu tun hat

Systeme, die still versagen, sind in der Praxis das härteste Problem — egal ob es sich um ein Kamera-Backup-System, eine mobile App oder einen Hintergrundprozess in einem Geschäftssystem handelt. Ein Absturz fällt auf. Ein Prozess, der läuft aber nichts tut, bleibt wochenlang unbemerkt.

Wenn du eine App bauen lässt, lohnt es sich, diese Frage früh zu stellen: “Wie merkt ihr, dass das System still versagt?” Wer darauf keine klare Antwort hat, hat das Problem noch nicht durchdacht. Auf meiner Leistungsseite beschreibe ich, wie ich solche Fragen von Anfang an in die Architektur einbaue.

Nächste Woche: AutoBrew bekommt wahrscheinlich etwas Aufmerksamkeit. Was genau, entscheide ich Montagmorgen — je nachdem, was der App-Store-Review-Prozess bis dahin mit dem aktuellen Build gemacht hat.

Teilen