Zurück zum Blog

24 Tage unsichtbar

24 Tage unsichtbar

Datum 15. August 2026
Kategorie Monitoring & Betrieb
Lesezeit 9 Minuten

Gefunden beim Aufräumen

Der Anlass war banal. Die Festplatte des Servers lief langsam voll, ich räumte alte Docker-Images weg und ging danach den Rest des Systems durch: anstehende Paket-Updates, Kernel-Stand, laufende Dienste. Dabei fiel der Blick auf die Agent-Liste des SIEM.

Ein Rechner stand dort auf Disconnected. Der letzte Lebenszeichen-Ping war 24 Tage alt.

Der Rechner selbst war völlig in Ordnung. Er lief, war über das VPN erreichbar, Ping 47 Millisekunden, seine anderen Dienste antworteten normal. Nur das SIEM hatte ihn seit dem 21. Juli nicht mehr gesehen. Drei Wochen lang war ein Endpoint ohne Logauswertung, ohne Dateiintegritätsprüfung, ohne Rootkit-Scan. Und niemand hatte es bemerkt.

Die Ursache war schnell gefunden und ziemlich unspektakulär: Der Agent versuchte permanent, sich neu anzumelden, und scheiterte an einem falschen Registrierungspasswort. Rund 1550 Fehlversuche pro Tag, etwa alle 56 Sekunden einer. Alle sauber protokolliert. Alle folgenlos.

Interessant an diesem Vorfall ist nicht der kaputte Agent. Interessant ist, warum drei verschiedene Sicherheitsnetze ihn nicht aufgefangen haben.

Zwei Signale, die es die ganze Zeit gab

Beim Nachsehen stellte sich heraus: Die Information lag doppelt vor.

Erstens hatte das SIEM selbst eine Regel dafür. Sie heißt schlicht „Agent disconnected" und hat gefeuert, mehrfach. Sie ist mit Stufe 3 eingestuft, die Benachrichtigungsschwelle steht bei 10. Sie landete also in der Datenbank und sonst nirgends.

Zweitens gab es längst eine Metrik in der Zeitreihen-Datenbank, die schlicht zählt, wie viele Agents gerade abgemeldet sind. Der Wert stand über Wochen konstant auf 1. Es hatte nur nie jemand eine Alarmregel darauf gelegt.

Das ist der Teil, der mich am meisten beschäftigt hat. Es war kein Datenproblem. Die Erkennung war vollständig vorhanden, sie war nur an keinen Empfänger angeschlossen. Gesammelte Daten sind noch keine Überwachung.

Warum der offensichtliche Alarm der falsche war

Der naheliegende Griff wäre gewesen, die vorhandene SIEM-Regel von Stufe 3 auf 10 zu heben. Fertig, Alarm eingerichtet.

Ein Blick auf die Häufigkeit hat das verhindert. In den drei Tagen davor hatte diese Regel 60 Mal gefeuert. Nicht ein einziges Mal wegen des kaputten Agents, sondern ausnahmslos wegen eines Laptops, der abmeldet, sobald man ihn zuklappt.

Eine hochgestufte Regel hätte also rund zwanzig Meldungen am Tag erzeugt, alle harmlos, und der echte Fall wäre genau darin untergegangen. Nach zwei Wochen hätte niemand mehr hingesehen. Man tauscht dann einen blinden Fleck gegen einen, den man sich selbst antrainiert.

Dieselbe Schwäche hat der vorhandene Summenzähler. Er sagt, dass ein Agent fehlt, aber nicht welcher. Ein schlafender Laptop und ein seit Wochen toter Server sehen darin gleich aus.

Die Lösung war deshalb, die Datenlage zu verbessern statt die Schwelle zu verbiegen. Der Metrik-Exporter liefert jetzt eine eigene Zeitreihe pro Agent, mit Namen und mit dem Alter des letzten Lebenszeichens. Damit lässt sich ein Laptop anders behandeln als ein Server: Bei Servern schlägt der Alarm nach einer Stunde an, beim Laptop erst nach sieben Tagen. Reboots und der wöchentliche Update-Lauf dauern Minuten und lösen nichts aus.

Der Test, der die neue Regel widerlegte

Damit hätte die Sache erledigt sein können. Regel geschrieben, Syntax geprüft, Ausdruck gegen die Live-Daten getestet, alles grün.

Stattdessen habe ich den Fall nachgestellt: Laptop zuklappen, 15 Minuten warten, aufklappen, hinterher die aufgezeichneten Werte ansehen. Das Ergebnis war unangenehm.

Alter des letzten Lebenszeichens:   1 s  ->  834 s   (steigt sauber)
Status-Feld des Agents:             durchgehend "active"

Das Statusfeld wechselte nicht. Die dokumentierte Schwelle des SIEM liegt bei zehn Minuten, überschritten waren sie um rund vier Minuten, und trotzdem galt der Agent weiter als aktiv. Das Feld wird träger gepflegt, als die Einstellung vermuten lässt.

Meine Regel hing genau an diesem Feld. Ich habe die beiden Varianten anschließend rückwirkend gegen die aufgezeichneten Messwerte laufen lassen:

Bedingung "Lebenszeichen älter als 600 s"   ->  4 Minuten am Stück wahr
Bedingung "Status ist nicht aktiv"          ->  nie wahr

Im Normalbetrieb sind beide Varianten still, und zwar gleichermaßen. Der Unterschied zeigt sich erst im Ernstfall, also genau dann, wenn man ihn nicht mehr prüfen kann. Die Regeln hängen jetzt am selbst berechneten Alter des letzten Lebenszeichens, nicht mehr am Statusfeld eines fremden Systems.

Die Lehre daraus ist unbequem: Eine Alarmregel auf einem Statusfeld, das ein anderes Programm pflegt, ist eine Annahme. Solange sie ungetestet ist, hat man nicht Überwachung, sondern das Gefühl von Überwachung.

Kommt der Alarm überhaupt an?

Der nächste Test lag damit auf der Hand. Dass eine Bedingung wahr wird, heißt noch nicht, dass jemand davon erfährt. Zwischen der Regel und dem Handy liegen mehrere Stationen, und keine davon war je unter realen Bedingungen geprüft worden.

Also eine befristete Testregel mit abgesenkter Schwelle, damit der zugeklappte Laptop den Alarm in Minuten statt in Tagen auslöst. Diesmal lief alles wie vorgesehen:

07:34  Regel wird "pending"   (Wartezeit greift)
07:35  Regel wird "firing"
09:34:54  Mail zugestellt und gespeichert
09:34:54  Push-Nachricht angenommen

Beide Kanäle auf dieselbe Sekunde. Die Testregel lag bewusst nur auf dem Server, nie im Repository, und wurde direkt danach wieder entfernt.

Ein Zwischenfall am Rande, den ich hier erwähne, weil er zur Ehrlichkeit gehört: Zwischendurch war ich überzeugt, die Push-Nachricht sei nicht angekommen, weil das Protokoll des Empfängers nichts zeigte. Tatsächlich hatte ich nur das Ende der Datei angesehen statt den Zeitraum des Alarms. Der Eintrag war die ganze Zeit da.

Der Melder meldete seinen eigenen Ausfall nicht

Bei diesem Test fiel etwas auf, das schwerer wiegt als der ursprüngliche Anlass. Um die Frage „ist die Mail rausgegangen?" zu beantworten, musste ich in die Protokolle des Mailservers sehen und die Zähler direkt im Container abfragen. In der Zeitreihen-Datenbank standen sie nicht.

Der Grund: Die Alarm-Verteilstelle war überhaupt kein Ziel der Metrik-Erfassung. Sie stellt ihre Zähler bereit, seit jeher, aber niemand hat sie je abgeholt. Es gab also keinen Verlauf, keine Historie und vor allem keine Möglichkeit, einen Zustellfehler überhaupt zu bemerken.

Der Ausfallmodus dahinter ist unangenehm leise. Wenn die Mail-Anmeldung abläuft, ein Zugangstoken rotiert wird oder die Brücke zum Push-Dienst stehen bleibt, dann feuern die Alarme munter weiter. Sie sind in der Oberfläche sichtbar, sie stehen auf firing, alles wirkt gesund. Sie kommen nur bei niemandem an. Und genau dieser Zustand meldet sich nicht von selbst.

Der Rest war schnell erledigt: Erfassung einschalten, zwei Regeln ergänzen. Eine für fehlgeschlagene Zustellungen über irgendeinen Kanal, eine für eine Konfiguration, die nach dem Neuladen nicht übernommen wurde. Bei einem Alarmweg gibt es keine tolerierbare Fehlerquote, deshalb genügt ein einziger Fehlschlag.

Eine dritte Regel habe ich bewusst weggelassen, obwohl sie naheliegt: „Alarme feuern, aber es geht nichts raus." Wiederholungsmeldungen sind auf ein bis vier Stunden eingestellt, längere Stille zwischen zwei Zustellungen ist also der Normalfall. Die Regel hätte zuverlässig das Falsche gemeldet.

Wie ich dabei das Monitoring abgeschossen habe

Beim Einbau dieser Erfassung habe ich den Dienst, um den es die ganze Zeit ging, für sechs Minuten lahmgelegt. Zwei Fallen, beide selbst gestellt.

Die Konfigurationsdatei ist als einzelne Datei in den Container eingebunden, nicht als Verzeichnis. Ich hatte die geprüfte neue Fassung mit mv über die alte geschoben. Das erzeugt eine neue Inode. Der Container hält aber die alte, und die bleibt bestehen, auch wenn sie im Dateisystem keinen Namen mehr hat. Auf dem Server stand die neue Konfiguration, im Container die alte, und das Neuladen meldete brav Erfolg. Sichtbar wird das nur, wenn man die Inode-Nummern auf beiden Seiten vergleicht.

Der Neustart zur Behebung löste die Einbindung neu auf und deckte damit die zweite Falle auf: Die per scp übertragene Datei war nur für den Eigentümer lesbar. Der Dienst läuft unprivilegiert, konnte seine eigene Konfiguration nicht öffnen und ging in eine Neustartschleife. Die Datei selbst war fehlerfrei, die vorherige Syntaxprüfung hatte sie bestätigt.

# statt mv: Inhalt ersetzen, Inode behalten, Rechte setzen
cat neu.yml > aktiv.yml && chmod 644 aktiv.yml

# und danach im Container nachsehen, ob die Änderung wirklich ankam
grep -c "neue Zeile" /pfad/im/container/aktiv.yml

„Neuladen erfolgreich" ist eben keine Bestätigung, dass die Änderung wirkt. Erst der Blick von innen zählt. Bei Verzeichnis-Einbindungen tritt das Problem übrigens nicht auf, dort hatte dasselbe Vorgehen den ganzen Abend über einwandfrei funktioniert.

Der Vorfall passt unfreiwillig gut zum Thema. Ein Alarmweg, dessen Ausfall niemand meldet, ist kein theoretisches Risiko. Ich habe ihn an diesem Abend selbst erzeugt, zufällig genau bei der Arbeit, die ihn schließen sollte.