Sistema di gestione della configurazione della rete di filtraggio Qrator

Sistema di gestione della configurazione della rete di filtraggio Qrator

TL;DR: Descrizione dell'architettura client-server del nostro sistema interno di gestione della configurazione di rete, QControl. Si basa su un protocollo di trasporto a due livelli che lavora con messaggi compressi in gzip senza decompressione tra gli endpoint. Router distribuiti ed endpoint ricevono aggiornamenti di configurazione, e il protocollo stesso consente l'installazione di relay intermedi localizzati. Il sistema è costruito secondo il principio di backup differenziale (“recent-stable”, spiegato di seguito) e utilizza il linguaggio di query JMESpath insieme al template engine Jinja per il rendering dei file di configurazione.

Qrator Labs gestisce una rete globale distribuita di mitigazione degli attacchi. La nostra rete funziona secondo il principio anycast, e le subnet vengono annunciate tramite BGP. Essendo una rete BGP anycast, fisicamente situata in diverse regioni del pianeta, possiamo elaborare e filtrare il traffico illegittimo più vicino al nucleo di Internet — verso gli operatori Tier-1.

D'altra parte, gestire una rete distribuita geograficamente non è semplice. La comunicazione tra i punti di presenza della rete è fondamentale per un fornitore di servizi di sicurezza, per garantire una configurazione coerente di tutti i nodi della rete, aggiornandoli in modo tempestivo. Pertanto, per fornire il massimo livello possibile di servizio principale per il consumatore, è stato necessario trovare un modo per sincronizzare in modo affidabile i dati di configurazione tra i continenti.

In principio era la Parola. Questa è rapidamente diventata un protocollo di comunicazione che necessitava di un aggiornamento.


Il fondamento dell'esistenza di QControl e, allo stesso tempo, la principale ragione per cui viene speso un numero significativo di tempo e risorse per costruire un protocollo di questo tipo, è la necessità di avere una fonte di configurazione unica e autorevole e, in ultima analisi, di sincronizzare con essa i nostri punti di presenza. Il repository stesso era solo uno dei molteplici requisiti durante lo sviluppo di QControl. In aggiunta, avevamo bisogno di integrazioni con servizi esistenti e pianificati presso i punti di presenza (PP), metodi intelligenti (e personalizzabili) per la validazione dei dati e anche la delimitazione degli accessi. Inoltre, volevamo gestire tale sistema tramite comandi, piuttosto che modificare i file manualmente. Prima di QControl, i dati venivano inviati ai punti di presenza praticamente a mano. Se uno dei punti di presenza non era disponibile e ci dimenticavamo di aggiornarlo in seguito, la configurazione risultava desincronizzata — e bisognava spendere tempo per ripristinarla.

Alla fine, abbiamo ideato il seguente schema:
Sistema di gestione della configurazione della rete di filtraggio Qrator
Il server di configurazione è responsabile della validazione dei dati e dell'archiviazione, il router ha diversi endpoint che ricevono e trasmettono gli aggiornamenti di configurazione dai clienti e dal team di supporto al server e dai server ai punti di presenza.

La qualità della connessione internet varia notevolmente in diverse parti del pianeta — per illustrare questo punto, diamo un'occhiata a un semplice MTR da Praga, Repubblica Ceca, a Singapore e Hong Kong.

Sistema di gestione della configurazione della rete di filtraggio Qrator
MTR da Praga a Singapore

Sistema di gestione della configurazione della rete di filtraggio Qrator
Stesso per Hong Kong

Alte latenze significano una minore velocità. Inoltre, c'è una perdita di pacchetti. La larghezza di banda non compensa questo problema, che deve essere sempre preso in considerazione quando si costruiscono sistemi decentralizzati.

La configurazione completa del punto di presenza è un volume considerevole di dati che devono essere inviati a molti destinatari su connessioni inaffidabili. Fortunatamente, anche se la configurazione cambia costantemente, ciò avviene in piccole porzioni.

Design recent-stable

Si può dire che costruire una rete distribuita secondo il principio degli aggiornamenti incrementali sia una soluzione piuttosto ovvia. Tuttavia, ci sono molte problematiche legate ai diff. È necessario mantenere tutti i diff tra i punti di riferimento e poterli inviare nel caso in cui qualcuno abbia perso parte dei dati. Ogni punto di destinazione deve applicarli in una sequenza rigorosamente definita. Di solito, nel caso di più punti di destinazione, tale operazione può richiedere molto tempo. Il destinatario deve anche essere in grado di richiedere le parti mancanti e, naturalmente, la parte centrale deve rispondere a tale richiesta in modo corretto, inviando solo i dati mancanti.

Alla fine, siamo arrivati a una soluzione abbastanza interessante: abbiamo solo uno strato di riferimento, fisso, che chiameremo stable, e solo un diff per esso — recent. Ogni recent si basa sull'ultimo stable formato ed è sufficiente per ricostruire i dati di configurazione. Una volta che un nuovo recent arriva a destinazione, il vecchio non è più necessario.

Rimane solo da inviare occasionalmente una nuova configurazione stabile, ad esempio quando il recente diventa troppo grande. È importante anche il fatto che inviamo tutti questi aggiornamenti in modalità broadcast/multicast, senza preoccuparci dei singoli destinatari e della loro capacità di raccogliere i pezzi di dati insieme. Una volta accertato che tutti hanno una configurazione stabile corretta, inviamo solo le novità recenti. Vale la pena precisare che funziona? Funziona. La configurazione stabile è memorizzata nella cache sul server di configurazione e dai destinatari, mentre le novità recenti vengono create secondo necessità.

Architettura di trasporto a due livelli

Perché abbiamo costruito il nostro trasporto su due livelli? La risposta è piuttosto semplice: volevamo separare il routing dalla logica di alto livello, estraendo ispirazione dal modello OSI con il suo livello di trasporto e livello applicativo. Come protocollo di trasporto abbiamo scelto Thrift, mentre per il formato di alto livello dei messaggi di controllo abbiamo utilizzato il formato di serializzazione msgpack. Per questo motivo, il router (che esegue multicast/broadcast/relay) non guarda all'interno di msgpack, non decomprime né ricompone il contenuto, ma esegue solo il trasferimento dei dati.

Thrift (dall'inglese ‘economia’, pronunciato come [θrift]) è un linguaggio di descrizione delle interfacce, utilizzato per definire e creare servizi per diversi linguaggi di programmazione. È un framework per la chiamata remota di procedure (RPC). Combina un pipeline software con un motore di generazione di codice per sviluppare servizi che funzionano in modo efficace e semplice tra diversi linguaggi.

Abbiamo scelto il framework Thrift per il supporto RPC e multi-linguaggio. Come al solito, i componenti principali sono stati il client e il server. Tuttavia, il router si è rivelato una sfida, in parte a causa dell'assenza di una soluzione pronta durante il nostro sviluppo.

Sistema di gestione della configurazione della rete di filtraggio QratorEsistono altre opzioni, come protobuf / gRPC, tuttavia, quando abbiamo avviato il nostro progetto, gRPC era ancora giovane e non ci siamo sentiti pronti ad adottarlo.

Certo, avremmo potuto (e in realtà sarebbe stato opportuno) creare una nostra soluzione. Sarebbe stato più semplice sviluppare un protocollo adatto alle nostre esigenze, poiché l'architettura client-server è relativamente diretta da implementare rispetto alla costruzione di un router su Thrift. In ogni caso, esiste un pregiudizio tradizionale verso protocolli e implementazioni personalizzati. Inoltre, si pone sempre la domanda: «Come trasferiremo questo su altri linguaggi?». Pertanto, abbiamo subito scartato l'idea di reinventare la ruota.

Msgpack è simile a JSON, ma più veloce e compatto. È un formato binario per la serializzazione dei dati, che consente lo scambio di dati tra molteplici linguaggi.

Al primo livello abbiamo Thrift con le informazioni minime necessarie per il router per inoltrare il messaggio. Al secondo livello ci sono le strutture imballate msgpack.

Abbiamo scelto msgpack perché è più veloce e compatto rispetto a JSON. Ma ciò che è ancora più importante, supporta tipi di dati personalizzati, permettendoci di utilizzare funzionalità interessanti come la trasmissione di dati binari raw o oggetti speciali che rappresentano l'assenza di dati, il che era fondamentale per il nostro schema “recent-stable”.

JMESPath
JMESPath è un linguaggio di interrogazione per JSON.
Questa è la descrizione che otteniamo dalla documentazione ufficiale di JMESPath, ma in realtà offre molto di più. JMESPath consente di cercare e filtrare sotto-alberi in una struttura ad albero arbitraria, oltre a applicare modifiche ai dati al volo. Inoltre, consente di aggiungere filtri speciali e procedure di trasformazione dei dati. Anche se, ovviamente, richiede un certo sforzo mentale per essere compreso.

Jinja
Per alcuni utenti, è necessario convertire la configurazione in un file, quindi utilizziamo un motore di template e Jinja rappresenta la scelta evidente. Con essa generiamo il file di configurazione a partire dal template e dai dati ottenuti in fase di destinazione.

Per generare il file di configurazione abbiamo bisogno di una query JMESPath, di un template per la posizione del file nel sistema operativo e di un template per il file di configurazione stesso. Inoltre, in questa fase è utile chiarire i diritti di accesso al file. Tutto questo è stato abilmente combinato in un unico file: prima dell'inizio del template di configurazione, inseriamo un'intestazione in formato YAML che descrive il resto.

Ad esempio:

---
selettore: "[@][?@.fft._meta.version == `42`] | items([0].fft_config || `{}`)"
nome_file_destinazione: "fft/{{ match[0] }}.json"
modalità_file: 0644
ricarica_daemons: [fft]
...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}

Per creare un file di configurazione per un nuovo servizio, dobbiamo aggiungere solo un nuovo file template. Non sono necessarie modifiche al codice sorgente o al software nei punti di presenza.

Cosa è cambiato con l'introduzione di QControl nelle operazioni? La prima e più importante cosa è una consegna coerente e affidabile degli aggiornamenti di configurazione a tutti i nodi della rete. In secondo luogo, abbiamo ottenuto un potente strumento per la verifica delle configurazioni e per apportare modifiche dalla nostra assistenza tecnica, oltre che dai consumatori del servizio.

Siamo riusciti a realizzare tutto ciò utilizzando lo schema di aggiornamenti recent-stable per semplificare la comunicazione tra il server di configurazione e i destinatari della configurazione. Utilizzando un protocollo a due livelli per supportare un modo di instradamento dei dati indipendente dal contenuto. Abbiamo integrato con successo un motore di generazione di configurazioni basato su Jinja in una rete distribuita di filtraggio. Questo sistema supporta un'ampia gamma di modalità di configurazione per la nostra periferia distribuita e variegata.

Grazie per l'aiuto nella scrittura del materiale. VolanDamrod, serenheit, NoN.

Versione inglese post.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster