Zum Inhalt springen

Blog ·

Ein Kanarienvogel, der gegen echte Server läuft

Ein Stub kann Authentifizierung nicht beweisen. Zwei Monitoringfehler, die nur echte Server zeigen konnten, und was eine Statusseite zählen darf.

Von Provibr

Eine Monitoringprüfung, die sich an einer Datenbank anmeldet, ist ein kleines Stück Protokollcode mit einer unangenehmen Eigenschaft: fast jede Art, sie zu prüfen, prüft das Falsche.

Die naheliegende Prüfung ist ein Stub-Server, der das Protokoll spricht. Das funktioniert und beweist eine Menge: Rahmenbildung, Zeitüberschreitungen, was geschieht, wenn die Verbindung mitten im Handschlag abbricht, ob eine Zurückweisung den richtigen Fehlercode ergibt. Was er nicht beweisen kann, ist die Authentifizierung, und zwar aus einem strukturellen Grund. Ein Stub, der unser eigenes Verwürfeln nachrechnet, benutzt unsere eigene Umsetzung auf beiden Seiten der Leitung. Vertauschen Sie zwei Argumente in einem Hash, dann vertauschen beide Seiten sie, und der Test ist grün auf Code, den kein echter Server akzeptieren würde.

Diese Lücke stand als offener Punkt am Ende der vorigen Monitoringrunde: SCRAM war nur je gegen eine Umsetzung gemessen worden, die wir ebenfalls selbst geschrieben hatten. Ein Kanarienvogel schließt sie.

Was ein Kanarienvogel hier ist

Ein Kanarienvogel ist eine kleine Menge von Prüfungen, die gegen die echte Sache laufen statt gegen ein Modell davon. Wegwerfcontainer auf unserem eigenen Server, an localhost gebunden, mit einem echten PostgreSQL 17, Redis 7, MySQL 8.4 und MariaDB 11 darin. Die Adapter werden unmittelbar aufgerufen und nicht über den Befehlsumschlag, denn Gegenstand ist die Leitung und nicht die Verrohrung darum herum. Elf Prüfungen, allesamt standardmäßig übersprungen, und jede weigert sich zu laufen, solange ihre Adresse nicht wirklich gesetzt ist.

Diese zweite Sperre ist neben der ersten nicht überflüssig. Läuft die ganze Sammlung mit aufgehobenem Überspringen, würde eine Prüfung sonst stillschweigend zu 127.0.0.1:0 verbinden und einen Fehler ergeben, der wie ein Bug in unserem Code aussieht statt wie eine fehlende Umgebungsvariable.

Was die elf abdecken:

ServerWas bewiesen wird
PostgreSQL 17SCRAM mit richtigem und mit falschem Passwort, md5, trust, TLS verlangt gegen einen Server mit abgeschaltetem TLS, volle Zertifikatsprüfung gegen einen fremden Anker
Redis 7Passwortanmeldung und ein ACL-Benutzer, dazu die INFO-Erlaubnisliste
MySQL 8.4caching_sha2 bei kaltem und bei warmem Zwischenspeicher, dazu ein falsches Passwort
MariaDB 11.8Derselbe Handschlag gegen eine andere Umsetzung davon

Der Unterschied zwischen kaltem und warmem Zwischenspeicher zählt, weil es zwei verschiedene Codepfade sind. MySQL benutzt bei caching_sha2_password ein billiges Verwürfeln, sobald der Server das Konto schon einmal gesehen hat, und verlangt beim ersten Mal nach einem Neustart volle Authentifizierung. Nur ein echter Server startet neu.

Zwei Fehler, die kein Stub hätte zeigen können

PostgreSQL, falsches Passwort. Ist das Passwort falsch, antwortet PostgreSQL auf die erste SASL-Antwort nicht mit einer Fortsetzung. Es antwortet mit einem Fehler, der den SQLSTATE 28P01 trägt, ungültiges Passwort. Der Adapter hatte dort eine schlichte Zerlegung stehen, die alles, was keine Fortsetzung war, zu einem Protokollfehler machte: dieser Dienst hat nicht wie ein PostgreSQL-Server geantwortet. Der SQLSTATE wurde weggeworfen. Eine Kundin, deren Monitoringzugangsdaten abgelaufen waren, las, ihre Datenbank sei kaputt.

Ein Stub kommt dort nie vorbei, denn ein Stub, der den glücklichen Pfad prüft, schickt die Fortsetzung. Beide Stellen, an denen jetzt ein Fehler eintreffen kann, nach dem ersten und nach dem zweiten Schritt, lassen ihn einen Anmeldefehler bleiben.

Redis, ein leeres Feld. Der Adapter liest genau zwei Felder aus INFO: die Version und die Rolle. Er fragte den Abschnitt server ab, und role steht nicht in diesem Abschnitt. Es steht in replication. Das Feld war seit der Runde leer, die es einführte, und nichts sagte das, denn der Stub antwortete mit einem INFO-Block, den wir selbst geschrieben hatten und in dem role hilfsbereit dort stand, wo wir es erwarteten.

Die Behebung sind zwei getrennte INFO-Aufrufe und nicht ein Aufruf, der beide Abschnitte nennt, denn Redis nimmt mehrere Abschnitte in einem INFO erst ab Version 7.0 an. Auf 6.x ist das ein Syntaxfehler, und weil ein misslungenes INFO die Prüfung bewusst nicht rot macht, wäre das Feld auf älteren Servern schlicht wieder leer geblieben. Eine stille Verschlechterung, die eine stille Verschlechterung ersetzt.

Beide Fehler haben dieselbe Gestalt. Keiner von beiden erzeugte einen Fehler. Der eine erzeugte den falschen und der andere gar keinen, und beide waren für eine Testsammlung unsichtbar, die grün war.

MySQL und die Bibliothek, die nicht dazukam

Dieselbe Runde fügte MySQL und MariaDB als Prüfungsart hinzu. Dahinter steckt keine Clientbibliothek, und das ist eine bewusste Wiederholung einer früheren Entscheidung und keine neue.

Für PostgreSQL war die Wahl gewesen, kein eigenes SCRAM zu schreiben, denn SCRAM kennt einen Schritt, in dem der Server etwas signiert, das die Clientseite prüfen soll, und eine Umsetzung, die diesen Schritt auslässt, ist gegen einen echten Server grün und gegen einen gefälschten ebenso. Das ist die gefährliche Art von Abkürzung: sie scheitert nie laut.

MySQL kennt einen solchen Schritt nicht. Die native Passwortanmeldung ist eine Kette von SHA1-Hashes und der schnelle Pfad von caching_sha2_password eine Kette von SHA256-Hashes, und in keiner von beiden signiert der Server etwas, dessen Prüfung wir unterlassen könnten. Übrig bleiben Rahmenbildung und zwei Hashes, und beide Hashbibliotheken standen bereits im Baum. Ein Abfragebauer war ebenfalls nicht nötig: es gibt eine Abfrage, SELECT 1, sie steht als Konstante da, und ihre erwartete Antwort ebenso.

Letzteres liest sich wie eine fehlende Funktion. Über einer festen Abfrage ist eine einstellbare Erwartung ein Knopf, bei dem jede Stellung außer der Voreinstellung die Prüfung scheitern lässt, und ein solcher Knopf lehrt eine Betreiberin, dass Knöpfe nichts bedeuten. Er gehört dorthin an dem Tag, an dem es eine zweite feste Abfrage gibt, und keinen Tag früher. Die Version ist auch keine zweite Abfrage: MySQL schickt sie ungefragt im Handschlagpaket mit, und die zweite Abfrage ist immer die, an der in einem Jahr ein Freitextfeld wächst.

Zwei Dinge werden rundheraus abgelehnt. Die Clientfähigkeit, mit der ein Server die Clientseite anweisen kann, eine Datei von deren Platte zu lesen und hochzuladen, wird nicht angefordert, und das zugehörige Antwortbyte ist ein Protokollfehler statt ein Zweig, der etwas tut. Und volle caching_sha2-Authentifizierung erfolgt ausschließlich über TLS: die Alternative auf einer nackten Leitung ist, den öffentlichen Schlüssel des Servers über eben diese nicht authentifizierte Leitung zu holen, und wer dazwischensitzt, beantwortet das mit seinem eigenen Schlüssel. Die Ablehnung benennt die Reparatur, und die lautet: TLS einschalten.

Eine Statusseite zählt Ticks, keine Ergebnisse

Dieselbe Runde stellte eine öffentliche Statusseite vor all das, und die Rechnerei darauf verlangte eine Entscheidung, die leicht falsch ausfällt.

Jede geschuldete Ausführung einer Regel ist ein Tick. Ein Tick kann als up, degraded oder down enden, und er kann auch als source_unavailable enden (unsere eigene Messquelle war nicht da), als configuration_error (die Regel taugte nicht), als unsupported (die Agentin kannte diese Prüfungsart nicht) oder als versäumt. Diese letzten vier sagen nichts über den Dienst der Kundschaft.

Wäre der Nenner einer Tagesverfügbarkeit jeder geschuldete Tick, dann senkte eine Störung von uns die Zahl auf der Statusseite der Kundschaft. Der Nenner ist deshalb up plus degraded plus down: die Ticks, in denen wirklich jemand auf den Dienst geschaut hat. Bleibt davon nichts übrig, dann lautet die Antwort für diesen Tag, dass es keine Antwort gibt, ausdrücklich nicht null und ausdrücklich nicht hundert Prozent. Auf der Seite selbst erscheinen diese vier Zustände als unbekannt und nie als down.

Degraded zählt halb. Das ist eine Wahl und kein Naturgesetz: ein Dienst, der den ganzen Tag antwortet, aber über seiner Latenzschwelle liegt, ist weder ausgefallen noch in Ordnung, und ihn zu einer der beiden Seiten zu zählen macht eine von beiden bedeutungslos.

Lücken werden als Lücken gezeigt. Eine Regel, die vorige Woche angelegt wurde, hat keine neunzig Tage Verlauf, und ein Balken, der das mit dem Wert des Nachbartages auffüllt, behauptet etwas, das nie gemessen wurde.

Was uns eine grüne Testsammlung gekostet hat

Jeder der obigen Fehler saß unter einem bestandenen Test. Die Stub-Sammlung war nicht falsch; sie beantwortete eine andere Frage als die, die alle für gestellt hielten. Der Wert des Kanarienvogels ist nicht, dass er eine bessere Prüfung wäre. Er ist, dass die andere Seite der Leitung von jemandem geschrieben wurde, der unsere Annahmen nicht teilt.

Das ist sechs Wegwerfcontainer wert, und es lohnt sich aufzuschreiben, dass sie Wegwerfcontainer sind: sie laufen auf unserer eigenen Maschine, sie werden beim Beenden entfernt, und die Prüfungen weigern sich zu laufen, wenn die Adressen fehlen.

Aus der Monitoringarbeit von Provibr. Warum eine gehostete Sonde genau eine Art Nachricht annimmt, steht in eine Sonde, die sich zu nichts überreden lässt, und warum ein Monitoringgeheimnis nur einmal reist, steht in ein Geheimnis, das einmal reist.

Fangen Sie mit einem Host und einem Agenten an

Konto erstellen, den Agenten mit einem einzigen Befehl installieren und Ihren ersten Proxmox-Host verbinden. Die Testphase läuft 14 Tage und fragt nicht nach Zahlungsdaten.