Qrator-Filtersystem zur Netzwerkkonfigurationsverwaltung

Qrator-Filtersystem zur Netzwerkkonfigurationsverwaltung

TL; DR: Beschreibung der Client-Server-Architektur unseres internen Netzwerkkonfigurationsmanagementsystems QControl. Es basiert auf einem zweischichtigen Transportprotokoll, das mit gzip-gepackten Nachrichten ohne Dekomprimierung zwischen Endpunkten funktioniert. Verteilte Router und Endpunkte erhalten Konfigurationsaktualisierungen und das Protokoll selbst ermöglicht die Installation lokalisierter Zwischenrelais. Das System ist nach dem Prinzip aufgebaut Differenzielle Sicherung („recent-stable“, unten erklärt) und verwendet die JMESpath-Abfragesprache zusammen mit der Jinja-Templating-Engine, um Konfigurationsdateien zu rendern.

Qrator Labs betreibt ein weltweit verteiltes Netzwerk zur Angriffsabwehr. Unser Netzwerk arbeitet nach dem Anycast-Prinzip und Subnetze werden über BGP angekündigt. Da wir ein BGP-Anycast-Netzwerk sind, das sich physisch in mehreren Regionen der Erde befindet, können wir illegalen Datenverkehr näher am Kern des Internets – den Tier-1-Betreibern – verarbeiten und filtern.

Andererseits ist es nicht einfach, ein geografisch verteiltes Netzwerk zu sein. Die Kommunikation zwischen Netzwerk-Präsenzpunkten ist für den Sicherheitsdienstanbieter von entscheidender Bedeutung, um eine konsistente Konfiguration aller Netzwerkknoten zu gewährleisten und diese zeitnah zu aktualisieren. Um dem Verbraucher den größtmöglichen Kernservice zu bieten, mussten wir daher einen Weg finden, Konfigurationsdaten zuverlässig über Kontinente hinweg zu synchronisieren.

Am Anfang war das Wort. Es entwickelte sich schnell zu einem Kommunikationsprotokoll, das einer Aktualisierung bedarf.


Der Eckpfeiler der Existenz von QControl und gleichzeitig der Hauptgrund für den erheblichen Zeit- und Ressourcenaufwand für den Aufbau dieser Art von Protokoll ist die Notwendigkeit, eine einzige maßgebliche Konfigurationsquelle zu erhalten und letztendlich unsere Präsenzpunkte zu synchronisieren damit. Der Speicher selbst war nur eine von mehreren Anforderungen bei der Entwicklung von QControl. Darüber hinaus benötigten wir auch Integrationen mit bestehenden und geplanten Diensten an Points of Presence (POP), intelligente (und anpassbare) Methoden zur Datenvalidierung sowie Zugangskontrolle. Darüber hinaus wollten wir ein solches System auch über Befehle steuern, anstatt Änderungen an Dateien vorzunehmen. Vor QControl wurden Daten fast manuell an Points of Presence gesendet. Wenn einer der Präsenzpunkte nicht verfügbar wäre und wir vergessen hätten, ihn später zu aktualisieren, wäre die Konfiguration nicht mehr synchron und wir müssten Zeit damit verschwenden, sie wieder zum Laufen zu bringen.

Als Ergebnis haben wir folgendes Schema entwickelt:
Qrator-Filtersystem zur Netzwerkkonfigurationsverwaltung
Der Konfigurationsserver ist für die Datenvalidierung und -speicherung verantwortlich; der Router verfügt über mehrere Endpunkte, die Konfigurationsaktualisierungen von Clients und Supportteams empfangen und an den Server sowie vom Server an Points of Presence senden.

Die Qualität der Internetverbindung ist auf der ganzen Welt immer noch sehr unterschiedlich. Um dies zu veranschaulichen, schauen wir uns einen einfachen MTR von Prag (Tschechische Republik) nach Singapur und Hongkong an.

Qrator-Filtersystem zur Netzwerkkonfigurationsverwaltung
MTR von Prag nach Singapur

Qrator-Filtersystem zur Netzwerkkonfigurationsverwaltung
Das Gleiche gilt für Hongkong

Hohe Latenz bedeutet geringere Geschwindigkeit. Darüber hinaus kommt es zu Paketverlusten. Die Kanalbreite gleicht dieses Problem nicht aus, was beim Aufbau dezentraler Systeme immer berücksichtigt werden muss.

Die vollständige Konfiguration eines Point of Presence stellt eine erhebliche Datenmenge dar, die über unzuverlässige Verbindungen an viele Empfänger gesendet werden muss. Obwohl sich die Konfiguration ständig ändert, geschieht dies glücklicherweise in kleinen Schritten.

Aktuelles, stabiles Design

Wir können sagen, dass der Aufbau eines verteilten Netzwerks nach dem Prinzip der inkrementellen Aktualisierung eine ziemlich naheliegende Lösung ist. Aber es gibt viele Probleme mit Diffs. Wir müssen alle Unterschiede zwischen den Referenzpunkten speichern und sie auch erneut senden können, falls jemand einen Teil der Daten übersehen hat. Jedes Ziel muss sie in einer genau festgelegten Reihenfolge anwenden. Typischerweise kann ein solcher Vorgang bei mehreren Zielen lange dauern. Der Empfänger muss auch in der Lage sein, die fehlenden Teile anzufordern und natürlich muss die zentrale Stelle auf eine solche Anfrage korrekt reagieren und nur die fehlenden Daten senden.

Als Ergebnis kamen wir zu einer ziemlich interessanten Lösung – wir haben nur eine Referenzebene, fest, nennen wir sie stabil, und nur ein Diff dafür – aktuell. Jeder aktuelle Wert basiert auf dem zuletzt generierten Stable und reicht aus, um die Konfigurationsdaten neu zu erstellen. Sobald das frische Exemplar seinen Bestimmungsort erreicht, wird das alte nicht mehr benötigt.

Es bleibt nur noch, von Zeit zu Zeit eine neue stabile Konfiguration zu senden, beispielsweise weil die aktuelle zu groß geworden ist. Wichtig hierbei ist auch, dass wir alle diese Aktualisierungen im Broadcast-/Multicast-Modus versenden, ohne uns Gedanken über einzelne Empfänger und deren Fähigkeit zu machen, Datenstücke zusammenzusetzen. Sobald wir sicher sind, dass jeder über den richtigen Stall verfügt, versenden wir nur neue, aktuelle Exemplare. Lohnt es sich klarzustellen, dass dies funktioniert? Funktioniert. „Stable“ wird auf dem Konfigurationsserver und den Empfängern zwischengespeichert, „Recent“ wird bei Bedarf erstellt.

Architektur des zweistufigen Transports

Warum haben wir unseren Transport auf zwei Ebenen aufgebaut? Die Antwort ist ganz einfach: Wir wollten das Routing von der High-Level-Logik entkoppeln und uns dabei vom OSI-Modell mit seinen Transport- und Anwendungsschichten inspirieren lassen. Wir verwendeten Thrift für die Rolle des Transportprotokolls und das msgpack-Serialisierungsformat für das High-Level-Format von Steuernachrichten. Aus diesem Grund schaut der Router (der Multicast/Broadcast/Relay durchführt) nicht in msgpack, entpackt oder packt den Inhalt nicht zurück und leitet nur Daten weiter.

Thrift (aus dem Englischen – „thrift“, ausgesprochen [θrift]) ist eine Schnittstellenbeschreibungssprache, die zum Definieren und Erstellen von Diensten für verschiedene Programmiersprachen verwendet wird. Es handelt sich um ein Framework für Remote Procedure Calls (RPC). Kombiniert eine Software-Pipeline mit einer Codegenerierungs-Engine, um Dienste zu entwickeln, die mehr oder weniger effizient und einfach zwischen Sprachen funktionieren.

Wir haben uns wegen RPC und der Unterstützung vieler Sprachen für das Thrift-Framework entschieden. Die einfachen Teile waren wie üblich der Client und der Server. Allerdings erwies sich der Router als eine harte Nuss, was unter anderem daran lag, dass es während unserer Entwicklung keine fertige Lösung gab.

Qrator-Filtersystem zur NetzwerkkonfigurationsverwaltungEs gibt auch andere Optionen, wie z. B. protobuf / gRPC. Als wir mit unserem Projekt begannen, war gRPC jedoch noch recht neu und wir trauten uns nicht, es zu übernehmen.

Natürlich könnten (und hätten wir auch) unser eigenes Fahrrad bauen können. Es wäre einfacher, ein Protokoll für das zu erstellen, was wir brauchen, da die Client-Server-Architektur im Vergleich zum Aufbau eines Routers auf Thrift relativ einfach zu implementieren ist. Auf die eine oder andere Weise gibt es traditionell eine Tendenz zu selbstgeschriebenen Protokollen und Implementierungen beliebter Bibliotheken (aus gutem Grund); außerdem kommt bei Diskussionen immer die Frage auf: „Wie werden wir das auf andere Sprachen portieren?“ Also haben wir die Idee eines Fahrrads sofort verworfen.

Msgpack ähnelt JSON, ist jedoch schneller und kleiner. Es handelt sich um ein binäres Datenserialisierungsformat, das den Datenaustausch zwischen mehreren Sprachen ermöglicht.

Auf der ersten Ebene haben wir Thrift mit den minimalen Informationen, die der Router benötigt, um die Nachricht weiterzuleiten. Auf der zweiten Ebene gibt es gepackte msgpack-Strukturen.

Wir haben uns für msgpack entschieden, weil es im Vergleich zu JSON schneller und kompakter ist. Aber was noch wichtiger ist: Es unterstützt benutzerdefinierte Datentypen, sodass wir coole Funktionen wie die Übergabe von Rohbinärdateien oder speziellen Objekten verwenden können, die das Fehlen von Daten anzeigen, was für unser „Recent-Stable“-Schema wichtig war.

JMESPfad
JMESPath ist eine JSON-Abfragesprache.
Genau so sieht die Beschreibung aus, die wir aus der offiziellen JMESPath-Dokumentation erhalten, aber tatsächlich leistet sie noch viel mehr. Mit JMESPath können Sie Teilbäume in einer beliebigen Baumstruktur suchen und filtern und Änderungen an Daten im Handumdrehen vornehmen. Außerdem können Sie spezielle Filter und Datentransformationsverfahren hinzufügen. Obwohl es natürlich eine Gehirnleistung erfordert, um es zu verstehen.

Jinja
Für einige Verbraucher müssen wir die Konfiguration in eine Datei umwandeln – daher verwenden wir eine Template-Engine und Jinja ist die offensichtliche Wahl. Mit seiner Hilfe generieren wir aus der Vorlage und den am Ziel empfangenen Daten eine Konfigurationsdatei.

Um eine Konfigurationsdatei zu generieren, benötigen wir eine JMESPath-Anfrage, eine Vorlage für den Dateispeicherort im FS und eine Vorlage für die Konfiguration selbst. Zu diesem Zeitpunkt empfiehlt es sich auch, die Dateiberechtigungen zu klären. All dies wurde erfolgreich in einer Datei zusammengefasst – vor dem Start der Konfigurationsvorlage haben wir einen Header im YAML-Format eingefügt, der den Rest beschreibt.

Zum Beispiel:

---
selector: "[@][[email protected]._meta.version == `42`] | items([0].fft_config || `{}`)"
destination_filename: "fft/{{ match[0] }}.json"
file_mode: 0644
reload_daemons: [fft] ...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}

Um eine Konfigurationsdatei für einen neuen Dienst zu erstellen, fügen wir lediglich eine neue Vorlagendatei hinzu. Es sind keine Änderungen am Quellcode oder der Software an den Points of Presence erforderlich.

Was hat sich geändert, seit QControl live gegangen ist? Das Erste und Wichtigste ist die konsistente und zuverlässige Bereitstellung von Konfigurationsaktualisierungen an alle Knoten im Netzwerk. Die zweite besteht darin, ein leistungsstarkes Tool zur Überprüfung der Konfiguration und zum Vornehmen von Änderungen daran durch unser Support-Team sowie durch Nutzer des Dienstes zu erhalten.

All dies konnten wir mithilfe des Recent-Stable-Update-Schemas erreichen, um die Kommunikation zwischen dem Konfigurationsserver und den Konfigurationsempfängern zu vereinfachen. Verwendung eines zweischichtigen Protokolls zur Unterstützung einer inhaltsunabhängigen Art der Datenweiterleitung. Erfolgreiche Integration einer Jinja-basierten Konfigurationsgenerierungs-Engine in ein verteiltes Filternetzwerk. Dieses System unterstützt eine Vielzahl von Konfigurationsmethoden für unsere verteilten und heterogenen Peripheriegeräte.

Vielen Dank für Ihre Hilfe beim Verfassen des Materials. VolanDamrod, Gelassenheit, Nicht.

Englische Version Post.

Source: habr.com

Kommentar hinzufügen