Zum Inhalt springen

Blog ·

Eine Sonde, die sich zu nichts überreden lässt

Überwachung von außerhalb des Kundennetzes bedeutet eigene Maschinen im Internet. So verhindert die Plattform, dass eine davon etwas anderes tut als messen.

Von Provibr

Zu prüfen, ob eine Kundenmaschine erreichbar ist, hat nur von einem Ort aus Sinn, an dem die Maschine nicht steht. Ein Agent im Netz selbst kann Ihnen sagen, dass der Dienst lauscht; er kann Ihnen nicht sagen, dass der Weg dorthin unterbrochen ist. Also stellt man eigene Maschinen ins öffentliche Internet und lässt sie die Verbindung aufbauen.

Das ist in einem Atemzug eine Funktion und ein Risiko. Eine Sonde ist Hardware, die uns gehört, in einem Rechenzentrum, das wir mit Fremden teilen, mit einer offenen Steuerverbindung zu unserer Plattform. Wenn irgendetwas auf dieser Verbindung sie dazu bringen kann, etwas anderes zu tun als zu messen, wird sie zum nützlichsten Brückenkopf im ganzen Produkt.

Eine Binärdatei, und das Token entscheidet, was daraus wird

Eine Sonde ist kein eigenes Programm. Sie ist der Agent, den unsere Kunden betreiben, gestartet mit probe enroll oder probe run statt mit run, auf derselben Überwachungsmaschinerie und demselben Sitzungs-RPC. Ein abgespeckter Build kann einen Hypervisor-Befehl überhaupt nicht ausführen, was sicherer klingt, aber es bedeutet eine zweite Umsetzung von Wiederholung, Backoff, Zertifikatswechsel und Prüfmaschinerie, und die zweite bekommt jeden Monat weniger Aufmerksamkeit. Wir haben eine Binärdatei behalten und die Grenze an eine Stelle verschoben, die sich prüfen lässt.

Der naheliegende Weg, eine Binärdatei sich als zwei Dinge einschreiben zu lassen, ist dann ein Feld in der Einschreibungsanfrage, das sagt, welches von beiden. Das ist der falsche Weg, und es lohnt sich, deutlich zu sagen, warum: Ein Client, der seine eigene Art angibt, gibt seine eigenen Rechte an, und jede spätere Prüfung ruht auf einer Zeichenkette, die das Geprüfte selbst geliefert hat.

Es gibt dieses Feld also nicht. Es gibt zwei Token-Tabellen: enrollment_tokens mit einem Fremdschlüssel auf agents, der nicht leer sein darf, und monitoring_probe_enrollment_tokens mit einem ebensolchen auf monitoring_probes. Ein Token liegt in genau einer davon, also folgt das, was eingeschrieben wird, aus etwas, das wir ausgegeben haben, und nicht aus etwas, das der Aufrufer gesagt hat. Beide Fehlschläge geben dieselbe Ablehnung zurück, denn unterschiedliche Meldungen würden einem Angreifer verraten, welche Art Token er gefunden hat.

Das Zertifikat, das zurückkommt, trägt einen Common Name der Form probe:<id> und als Subject Alternative Name eine URI ohne Organisation darin, während die eines Agenten den Kunden benennt. Das ist für den Betreiber gedacht, der ein Zertifikat liest, und es ist ausdrücklich nicht die Grenze: Ein Common Name ist eine Zeichenkette, die eine Zertifizierungsstelle hineinschreibt. Die Grenze ist, dass ein Fingerabdruck sich in agents oder in monitoring_probes auflöst, nie in beiden, denn das sind zwei Tabellen.

Ein Urteil für jede Nachricht statt einer Liste der erlaubten

Das Sitzungsprotokoll besteht aus zwei Umschlagnachrichten, von denen jede eine aus einer Auswahl von Alternativen trägt. Eine Erlaubnisliste, die nur benennt, was eine Sonde senden darf, bleibt für immer grün, auch an dem Tag, an dem jemand eine fünfte Nachrichtenart hinzufügt, und an diesem Tag gibt es eine Nachrichtenart, die nichts erwähnt und die deshalb auch nichts ablehnt. Die Tabellen im Gateway benennen deshalb jedes Feld beider Umschläge mit einem ausdrücklichen Urteil: zweiundzwanzig Einträge nach oben, von denen drei echte Nachrichten und eine Folgenummer angenommen werden, und vierzehn nach unten, von denen eine echte Anweisung und drei Sitzungsbestätigungen angenommen werden. Ein Test liest das erzeugte TypeScript und scheitert, wenn ein Feld kein Urteil trägt, und er scheitert auch andersherum, wenn ein Name in der Tabelle im Protokoll nicht mehr vorkommt, denn eine Regel, die nichts einschränkt, liest sich genau wie Abdeckung. Eine Sitzungsnachricht hinzuzufügen kostet jetzt eine Zeile, und diese Zeile ist eine Entscheidung mit dem Namen eines Menschen auf dem Commit.

Nach oben darf eine Sonde drei Dinge senden: Hallo, Herzschlag und ein Prüfergebnis. Die Ablehnung, die eine Erklärung verdient, sind Befehlsergebnisse: Die Verteilung ordnet ein Ergebnis einem Befehl über die Kennung zu und nicht über den Absender, eine Sonde, die eines senden könnte, könnte also einen Befehl abschließen, der irgendeinem Agenten in der Flotte gehört. Nach unten werden zehn Einträge abgelehnt, und vier davon sind der Grund für den ganzen Entwurf:

NachrichtWas sie tragen würdeWarum eine Sonde sie nie sehen darf
Zugangsdaten-ÜbergabePanel- und Hypervisor-Zugänge eines KundenEine Sonde hat keinen Tresor und keine Integration; es gibt keinen Zustand, in dem das etwas bedeutet
Zugangsdaten-LöschungDie Anweisung, einen davon zu löschenDerselbe Grund, umgekehrt
Netboot-AnweisungWas einer Maschine ausgeliefert wird, die über das Netz startetEine Sonde stellt nichts bereit
Automatisierung öffnenEine Sandbox, die Kundenskripte ausführtEine Sonde führt keinen Kundencode aus

Das Register, in dem keine Sonde steht

Die Tabelle oben ist die beweisbare Hälfte des Versprechens. Die tragende Hälfte ist, dass es keinen Weg gibt. Die Befehlsverteilung findet ihr Ziel über die Agentenkennung; Sonden liegen in einem eigenen Register, geschlüsselt über die Sondenkennung, und haben überhaupt keine Agentenkennung. Kein Nachschlagen kann dem Code, der eine Zugangsdaten-Übergabe verschickt, eine Sondensitzung zurückgeben, selbst wenn jemand eine neue Art Anweisung hinzufügte und die Tabelle vollständig vergäße.

Die einzige Funktion, die an eine Sonde schreibt, prüft das Urteil an der Quelle und nicht beim Empfänger, denn eine Nachricht, die durchgerutscht wäre, käme bei einer Maschine von uns an, die dieselbe Binärdatei betreibt wie ein Kundenagent, und die könnte sie schlicht ausführen. Sie führt außerdem eine Prüfung durch, die die Tabelle nicht ausdrücken kann: command wird angenommen, aber eine Befehlsanfrage ist selbst eine Auswahl aus vierundzwanzig Alternativen, von denen alle bis auf drei eine Familie von Anbieteranweisungen sind, ohne diese Prüfung hieße „eine Sonde darf Befehle empfangen“ also stillschweigend „eine Sonde darf einen Hypervisor-Befehl empfangen“. Die Nutzlast muss genau ein Feld sein, und dieses Feld muss die Überwachungsprüfung sein. Der Test läuft jedes Nutzlastfeld im erzeugten Code ab und behauptet, dass alle bis auf eines abgelehnt werden.

Eine Sonde wählen und auf die nächste ausweichen

Jeder Standort kann mehr als eine Sonde tragen, also muss etwas entscheiden. Ein Hash modulo der Zahl der Sonden ist an dem Tag falsch, an dem sich diese Zahl ändert: Fast jede Regel wandert auf einmal, und überall im Produkt springen Latenzkurven am selben Tag ohne sichtbaren Grund. Rendezvous-Hashing bewegt nur das, was an der Sonde hing, die verschwunden ist. Der Schlüssel besteht aus der Regelfassung und dem Standort. Die Fassung ist unveränderlich, eine Regel, die der Kunde bearbeitet, darf also berechtigterweise woanders landen, während die Wahl innerhalb einer Fassung nie abdriftet. Der Slot steht bewusst nicht im Schlüssel: Mit der Taktnummer darin würden aufeinanderfolgende Messungen auf verschiedenen Sonden landen, und ein Unterschied zwischen zwei Punkten könnte ebenso gut ein Unterschied zwischen zwei Maschinen sein. Vergleichbarkeit über die Zeit ist mehr wert als verteilte Last.

Eine Sonde kann Sie auf zwei Weisen im Stich lassen, die zu verschiedenen Zeitpunkten auffallen: Sie kann verschwunden sein, wenn Sie senden wollen, oder sie kann den Befehl annehmen und nie antworten, was erst zutage tritt, wenn das eigene Zeitlimit der Prüfung abgelaufen ist. Beides erzeugt einen weiteren Versuch innerhalb desselben Takts und nie einen zweiten Takt. Ein Ausweichen ist keine zweite Messung, und wäre es eine, dann verdoppelte ein Standort mit Pech still die Zahl der Stichproben. Die Obergrenze liegt bei zwei tatsächlichen Versuchen, denn zusammen müssen sie in die Frist des Takts passen.

Wenn ein Standort überhaupt keine gesunde Sonde hat, wird der Slot trotzdem beansprucht und die Quelle als nicht verfügbar festgehalten. Nie als ausgefallen: Dass ein Standort von uns fehlt, sagt nichts über die Maschine des Kunden, und es einen Ausfall zu nennen wäre ein Fehlalarm, den unsere eigene Infrastruktur erzeugt hat.

Drei Stellen, die eine private Adresse ablehnen

Eine Überwachungsregel ist ein Feld, in dem jemand ein Ziel benennt, und eine Maschine von uns verbindet sich jede Minute dorthin. Ohne Grenzen ist das eine Maschine zum Fälschen von Anfragen mit einer Rechnungsanschrift daran. Das Ziel ist eine Adresszuteilung, die zum eigenen Server des Kunden gehört, und kein freier Text, was den größten Teil des Problems beseitigt; der Rest ist eine Liste von Bereichen, die eine gehostete Sonde nicht berühren darf, zusätzlich zu denen, die überall geschlossen sind:

BereichWarum er geschlossen ist
10/8, 172.16/12, 192.168/16Privater Raum. Auf einer gehosteten Sonde ist das das Rechenzentrum und nicht der Kunde
100.64/10Carrier Grade NAT, wo Anbieter Lastverteiler und Verwaltungswege hängen haben
192.0.0/24, 192.0.2/24, 198.18/15, 198.51.100/24, 203.0.113/24, 192.88.99/24Protokollzuweisungen, Leistungsmessung, Dokumentation, ein abgeschafftes Relay-Anycast. Keiner dieser Bereiche kann eine Kundenmaschine sein; was auch immer antwortet, steht also näher als das Ziel
fc00::/7, 2001:db8::/32Die erste und die dritte Zeile oben, in IPv6: eindeutiger lokaler Raum und Dokumentationsraum

Abgebildete IPv4-Formen werden zuerst ausgepackt, sonst hat jede Regel oben eine Schreibweise, die an ihr vorbeigeht.

Die Prüfung läuft an drei Stellen, und alle drei werden gebraucht. Die Plattform lehnt vor dem Senden ab, ein schlechtes Ziel erreicht also nie eine Maschine, die es ausführen könnte. Die Sonde lehnt bei der Ausführung ab, denn erst dort ist bekannt, wozu ein Name tatsächlich aufgelöst hat, und das kann sich von Lauf zu Lauf unterscheiden. Und die Plattform liest die gemeldete Adresse noch einmal, wenn das Ergebnis eintrifft, und macht damit aus einer Sonde, die woanders gemessen hat, eine ungültige Antwort statt eines Datenpunkts. Ein privates Ziel ist ein Konfigurationsfehler und nie ein Fehlschlag: Es wurde nichts gemessen, es gibt also nichts über das Ziel zu sagen.

Was wir aufschreiben würden

  1. Lassen Sie den Client nie seine eigene Art benennen. Leiten Sie sie aus etwas ab, das Sie ausgegeben haben, und halten Sie beide Ablehnungen gleich, damit der Fehler kein Orakel ist.
  2. Zählen Sie das ganze Protokoll auf und nicht den erlaubten Teil. Lassen Sie einen Test den erzeugten Code lesen und ein Urteil je Feld verlangen, in beide Richtungen, damit eine neue Nachrichtenart ein roter Test ist und keine Lücke.
  3. Legen Sie die echte Grenze dorthin, wo sie nicht vergessen werden kann. Ein eigenes Register ohne Agentenkennung bedeutet, dass es keinen Weg gibt, was auch immer in der Tabelle steht.
  4. Prüfen Sie die Nutzlast ebenso wie den Umschlag. „Befehle sind erlaubt“ ist nicht dieselbe Aussage wie „dieser Befehl ist erlaubt“.
  5. Halten Sie die Uhr aus dem Zuordnungsschlüssel heraus, und machen Sie aus einem Ausweichen einen zusätzlichen Versuch und keine zusätzliche Stichprobe, damit eine Änderung in den Daten nie mit einer Änderung am Messgerät verwechselt werden kann.

Zur Klarheit: Gehostete Überwachung liegt hinter einem Rollout-Schalter, der standardmäßig aus ist, darunter gibt es keine Berechtigungsschicht, und nichts davon ist heute käuflich. Was unbewiesen ist, verdient ebenfalls einen Namen. Der Unterbefehl für die Sonde ist nie von Anfang bis Ende gelaufen, denn eine seiner Netzwerkkomponenten lässt sich auf der Maschine, auf der das geschrieben wurde, nicht übersetzen; die Standortregeln wurden nur in ihrer einfachsten Form ausgeübt; IPv6 wurde nirgends gemessen. Die Leitung selbst wurde echt betrieben, von der Einschreibung über eine Sitzung bis zu einer Prüfung, die zurückkam, und eine verbotene Nachricht auf dieser Verbindung wurde abgelehnt.

Aus der Überwachungsarbeit an Provibr. Warum ein Hostname bewiesen sein muss, bevor er ein Ziel sein kann, steht in wem ein Hostname gehört, und warum ein Incident nicht schließt, nur weil die Messungen aufgehört haben, steht in ein Incident, den Schweigen nicht schließt.

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.