Zum Inhalt springen

Blog ·

Ein echter Agent, ein Entwicklungs-Gateway

Fünf Runden endeten mit demselben Vorbehalt: über die Leitung unbewiesen. Das zu schließen kostete einen echten Agent und ein gepflanztes Passwort.

Von Provibr

Fünf Runden hintereinander endeten ihren Bericht mit einer Fassung desselben Satzes: Die Protokolländerung ist additiv, die Modultests sind grün, und nichts davon wurde gegen einen echten Agent ausgeübt. Monitoring-Adapter, der versiegelte Umschlag, automatische Agent-Aktualisierungen, ein Automatisierungsschritt, der auf einen Befehl wartet. Alle fünf waren richtig, soweit es jemand sehen konnte, und alle fünf trugen einen Vorbehalt, den die nächste Runde erbte.

Dieser Vorbehalt ist billig aufzuschreiben und er summiert sich. Jede Runde legt einen dazu, niemand liegt falsch, und nach fünf Malen liegt dort ein Stapel Verhalten, das nur je in einem Prüfaufbau existiert hat. Diese Runde diente dazu, den Stapel abzutragen statt ihn zu vergrößern: keine Funktion, keine Protokolländerung, keine Auslieferung. Zweiundachtzig Messungen gegen einen echten Agent.

Der Aufbau, in einem Satz

Ein echtes Agent-Programm, gebaut für dasselbe Ziel, das wir ausliefern, mit einem Entwicklungsschlüssel signiert, laufend in einem Wegwerfcontainer auf einer Maschine, die kein Entwicklerlaptop ist, im Gespräch mit einem Entwicklungs-Gateway über einen umgekehrten Tunnel, mit sieben Wegwerfdiensten auf der Rückschleife zum Messen und einer zweiten Kopie desselben Programms, eingeschrieben als gehostete Sonde.

Alles in diesem Satz ist tragend. Das Programm ist das ausgelieferte und kein Testbau, denn die interessanten Fehler wohnen in Serialisierung und TLS und nicht in der Logik. Das Gateway ist bewusst eine Entwicklungsinstanz auf abweichenden Ports, denn das Produktions-Gateway lauscht auf den naheliegenden, und ein Agent, der an der falschen Seite davon anklopft, wäre ein sehr schlechter Nachmittag. Die Dienste sind Wegwerfcontainer mit der echten Software in echten Versionen und keine Nachbildungen, denn der ganze Sinn dieser Übung ist, dass Nachbildungen genau das sind, was wir schon hatten.

Die Datenbankdienste erhielten ein Kanarienpasswort, und diese Wahl hat den nächsten Abschnitt möglich gemacht.

GruppeMessungenWas sie klärt
Monitoring-Adapter44Sechs Datenbank- und Verzeichnisprotokolle, richtige und falsche Zugangsdaten, und der folgende Regelzustand
Automatische Aktualisierungen19Planen, Anwenden, Neustarten und einen manipulierten Bau ablehnen
Automatisierungsschritt11Ein Befehl, der innerhalb der Wartezeit endet, und einer, der es nicht tut
Gehostete Sonde4Dasselbe Programm, eingeschrieben als Plattformsonde statt als Kundenagent
Quelle ausgefallen4Ein getrennter Agent erzeugt einen eigenen Zustand, nie eine falsche Störung

Eine Suche mit einem gepflanzten Wert

Die Behauptung, die wir am dringendsten prüfen wollten, ist eine Verneinung: Ein Monitoring-Geheimnis reist versiegelt und wird niemals in die Befehlszeile, das Ereignisprotokoll, den Nachrichtenbus oder eines der beiden Prozessprotokolle geschrieben.

Verneinungen sind die Behauptungen, die still danebengehen. Die Passwörter waren deshalb keine zufälligen Zeichenfolgen, deren Abwesenheit nichts beweist; es waren gepflanzte Werte, so gewählt, dass ein einziges Vorkommen irgendwo unverkennbar wäre. Danach wurden sieben Schichten durchsucht: die gespeicherten Befehlsnutzlasten, die Ereignistabelle, die gespeicherte Prüfkonfiguration, die gespeicherten Ergebnisse, der Nachrichtenbus in Text und in kodierter Form, das Gateway-Protokoll und das Agent-Protokoll. Zusammen sind das rund eine halbe Million Zeichen Material.

Null Treffer, was die gewünschte Antwort ist und zugleich die Antwort, die eine kaputte Suche gibt.

Dieselbe Suche wurde deshalb auf eine Datei losgelassen, die das Wort sicher enthielt, und fand es. Diese positive Kontrolle ist der Unterschied zwischen „wir haben gesucht und nichts gefunden“ und „wir haben nicht gesucht“. Sie kostet eine Zeile und ist die wertvollste Zeile im Skript.

Zwei weitere Bedingungen sichern dasselbe Versagen von der anderen Seite: Der Lauf muss mindestens ein Dutzend echte Prüfbefehle und mehrere Dutzend Busnachrichten beobachten, bevor er etwas schließen darf. Beide standen im Lauf der Runde einmal rot, bei Durchläufen, die sonst eine saubere Suche über gar nichts gemeldet hätten. Eine Suche, die in einer leeren Menge keine Verstöße findet, ist kein Beweis, und sie sieht genau wie ein Erfolg aus.

Zwei Arten, wie eine Messung grün ist, ohne zu messen

Beide sind in dieser Runde geschehen, in Skripten von jemandem, der diese Fehlerform kennt und regelmäßig darüber schreibt.

Die erste: Eine Abfrage wählte eine Spalte, und der Code las danach eine andere Eigenschaft der Ergebniszeilen. In einer typisierten Sprache ist das ein Fehler; hier ist es undefined, für immer, still, und jeder Vergleich damit scheitert so, dass es aussieht wie eine Zusicherung, die nie auslöst, statt einer, die immer zutrifft. Die Prüfung meldete Erfolg, weil sie nichts zu vergleichen hatte.

Die zweite: eine Zugehörigkeitsprüfung gegen eine leere Liste. Das ist eine Aussage über nichts, sie ist trivial zu erfüllen, und sie ist der natürliche Endzustand jedes Skripts, dessen frühere Stufe still null Zeilen lieferte. Beide sind billig abzusichern: die Form des Geholten prüfen und prüfen, dass die untersuchte Menge nicht leer ist.

Fünf Spaltennamen in diesen Skripten waren geraten statt nachgeschlagen. Die scheiterten lautstark, was die billige Fassung desselben Fehlers ist, aber es ist derselbe Fehler: Ein Befehl sagt, wie eine Spalte heißt, und die Fassung, in der eine Vermutung in einer nie ausgeführten Liste landet, ist die Fassung, welche die Durchsicht überlebt.

Ein Agent, der eine zweite Einschreibung ablehnt

Mitten in den Aktualisierungstests kam der Agent nicht mehr zurück. Von außen sah es wie eine Neustartschleife aus, und die erste Stunde ging in den Aktualisierungspfad.

Der Agent tat genau das Richtige. Die Einschreibung schreibt eine Zustandsdatei, und das erzeugte Installationsskript prüft auf diese Datei und lehnt eine erneute Einschreibung ab, wenn sie vorhanden ist. Einen bereits eingeschriebenen Host erneut einzuschreiben ersetzt eine Identität, und das versehentlich zu tun ist der Weg, auf dem eine Maschine in einem Schritt ihr Zertifikat und ihren Tresor verliert. Unsere für den Anlass geschriebene Aufsichtsschleife hatte kein solches Tor, und jeder Neustartversuch lief in die eigene, richtige Ablehnung des Agents.

Die Lehre handelt nicht von diesem Tor. Sie handelt davon, dass eine richtige Ablehnung und ein Defekt aus der Ferne dieselbe Form haben, und dass sie sich dadurch trennen lassen, dass man liest, was die Gegenseite wirklich gesagt hat, statt dessen, was das Symptom nahelegte.

Aus denselben Tagen kamen drei weitere Überraschungen, alle drei Entwurfsentscheidungen, die auf die Wirklichkeit trafen:

Geplante Aktualisierungszeiten liegen auf einem Fünfminutenraster, durchgesetzt von einer Datenbankbedingung. „In einer Minute“, was jemand beim Schreiben eines Tests möchte, gibt es nicht. Brauchbar wird das durch ein Nachholfenster: Ein verpasster Zeitpunkt wird noch eine Viertelstunde lang ausgeführt. Genau deshalb muss eine wiederholte Messung innerhalb derselben Viertelstunde einen unbenutzten Rasterzeitpunkt wählen, denn ein Zeitpunkt wird je Agent eindeutig beansprucht, und ein zweiter Lauf gegen denselben wird zu Recht abgelehnt.

Eine TCP-Prüfung steht standardmäßig auf TLS. Auf einen schlichten Port gerichtet scheitert sie mit einem TLS-Fehler, was richtig ist und wie eine kaputte Prüfung wirkt, bis man nachsieht, was ein Standardwert ist.

Und die Prüfzeile eines Automatisierungsschritts, dessen Wartezeit ablief, friert auf dem Zustand ein, den der Ablauf sah. Der Befehl wurde sechzig Millisekunden später zu einer Zeitüberschreitung, und die Zeile sagte weiter, was sie sagte. Das ist kein Fehler in der Zeile, es ist ein Fehler in der Bemerkung darüber, die versprach, dort das endgültige Ergebnis ablesen zu können. Dieses Versprechen wurde entfernt und andernorts sauber beantwortet.

Eine Meldung, die gruppiert, und eine Zählung, die null sagt

Der schärfste der kleinen Befunde. Nach einer erfolgreichen Aktualisierung schreibt die Plattform eine Meldung. Die Messung zählte Meldungen, die seit Beginn des Laufs angelegt wurden, und erhielt null.

Meldungen gruppieren. Ein zweites Vorkommen desselben erhöht einen Zähler auf der bestehenden Zeile und lässt deren Anlagezeitpunkt stehen, was in einem Panel genau richtig ist: „dieser Agent ist ausgefallen“ mit einer Zahl daneben statt vierzig Zeilen. Es ist auch genau das, was eine Zählung auf den Anlagezeitpunkt nichts melden lässt, während die Meldung sichtbar dasteht.

Die Messung lag falsch, das Produkt lag richtig, und der einzige Grund, warum wir wissen, in welche Richtung es ging, ist, dass jemand das Panel geöffnet hat, statt der Abfrage zu glauben. Jede Zusammenfassung hat eine Form, und ein Test, der die Form nicht kennt, prüft seine eigene Annahme.

Was wir aufschreiben würden

  1. „Über die Leitung unbewiesen“ ist eine Schuld, und sie summiert sich. Eine Runde kann sie tragen. Fünf Runden sind ein Stapel Verhalten, das außerhalb eines Prüfaufbaus nie existiert hat.
  2. Eine Verneinung mit gepflanztem Wert und positiver Kontrolle beweisen. Null Treffer und eine kaputte Suche sind ohne sie nicht zu unterscheiden.
  3. Jeder Suche eine Untergrenze geben. Prüfen, dass die Eingabe nicht leer ist, bevor daraus etwas geschlossen wird.
  4. Eine richtige Ablehnung sieht aus der Ferne wie ein Defekt aus. Lesen, was die Gegenseite sagte, bevor die eigene Seite umgeschrieben wird.
  5. Weder Spaltenname noch Standardwert noch Raster raten. Alle drei sind einen Befehl entfernt, und die Vermutung, die in einer Liste landet, ist die, die nie auslöst.
  6. Die Form des Gezählten kennen. Gruppierte Zeilen tragen nicht den Anlagezeitpunkt, nach dem gefiltert wird.

Zur Klarheit: In dieser Runde wurde nichts ausgeliefert. Keine Produktänderung, keine Protokolländerung, keine Agent-Auslieferung. Herausgekommen ist, dass fünf Vorbehalte weg sind und eine Handvoll kleiner Defekte behoben, und das Aufräumen wurde nachgezählt statt angenommen: keine übrigen Zeilen, keine übrigen Container, keine übrigen Dateien.

Einiges blieb unbewiesen. Der Einspeisungspfad der Automatisierung wurde über seinen Kern statt über das Panel ausgeübt, die Adapter liefen nicht von einer gehosteten Sonde, und es gibt weiterhin keine Messung gegen einen echten Hypervisor.

Aus der Plattformarbeit an Provibr. Warum das Monitoring-Geheimnis je Versuch versiegelt wird, steht in ein Geheimnis, das einmal reist, und wie eine signierte Agent-Aktualisierung einen manipulierten Bau ablehnt, steht in wie eine signierte Agent-Aktualisierung sich selbst ablehnt.

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.