Storia dei pacchetti DNS scomparsi dal supporto tecnico di Google Cloud

Dall'editor del blog Google: Ti sei mai chiesto come gli ingegneri Google Cloud Technical Solutions (TSE) gestiscono le tue richieste di supporto tecnico? Gli ingegneri del supporto tecnico TSE si occupano di identificare e risolvere i problemi segnalati dagli utenti. Alcuni di questi problemi sono piuttosto semplici, ma a volte ci sono richieste che richiedono l'attenzione di più ingegneri. In questo articolo, uno dei membri del team TSE ci parlerà di un problema bastante complicato che ha affrontato recentemente — un caso di pacchetti DNS mancanti. Durante questo racconto vedremo come gli ingegneri sono riusciti a risolvere la situazione e cosa hanno imparato nella risoluzione dell'errore. Speriamo che questa storia non solo ti racconti di un bug profondamente radicato, ma ti dia anche una comprensione dei processi coinvolti nella presentazione di una richiesta di supporto a Google Cloud.

Storia dei pacchetti DNS scomparsi dal supporto tecnico di Google Cloud

La risoluzione dei problemi è sia una scienza che un'arte. Tutto inizia con l'ipotesi sulla causa del comportamento anomalo del sistema, che poi viene testata. Tuttavia, prima di formulare un'ipotesi, dobbiamo chiaramente definire e articolare il problema. Se la domanda è troppo vaga, dovrai analizzare attentamente la situazione; è qui che entra in gioco l'«arte» della risoluzione dei problemi.

Nel contesto di Google Cloud, tali processi sono ulteriormente complicati, poiché Google Cloud si impegna a garantire la privacy dei propri utenti. Pertanto, gli ingegneri TSE non hanno accesso per modificare i tuoi sistemi né possono esaminare le configurazioni nello stesso modo in cui lo fanno gli utenti. Di conseguenza, per verificare una delle nostre ipotesi, noi (ingegneri) non possiamo modificare rapidamente il sistema.

Alcuni utenti pensano che noi correggeremo tutto come tecnici in un'officina, e ci inviano semplicemente l'id della macchina virtuale, mentre in realtà il processo avviene in forma di dialogo: raccolta di informazioni, formulazione e conferma (o smentita) di ipotesi, e alla fine la risoluzione del problema si basa sulla comunicazione con il cliente.

Il problema in questione

Oggi abbiamo davanti a noi una storia con un lieto fine. Una delle ragioni per il successo della risoluzione del caso proposto risiede nella descrizione molto dettagliata e precisa del problema. Qui sotto puoi vedere una copia del primo ticket (modificato, per nascondere informazioni riservate):
Storia dei pacchetti DNS scomparsi dal supporto tecnico di Google Cloud
In questo messaggio ci sono molte informazioni utili per noi:

  • È specificata una VM concreta
  • È specificato il problema stesso — il DNS non funziona
  • È indicato dove si manifesta il problema — VM e contenitore
  • Sono indicati i passi compiuti dall'utente per identificare il problema

La richiesta è stata registrata come «P1: Impatto Critico — Servizio non utilizzabile in produzione», il che significa monitoraggio costante della situazione 24/7 secondo lo schema «Follow the Sun» (puoi leggere di più su priorità delle richieste degli utenti), con un passaggio da un team di supporto all'altro ad ogni cambio di fuso orario. In sostanza, nel momento in cui il problema è arrivato al nostro team a Zurigo, aveva già fatto il giro del mondo. A quel punto l'utente aveva intrapreso misure per ridurre le conseguenze, ma temeva una ripetizione della situazione in produzione, poiché la causa principale non era ancora stata scoperta.

Al momento in cui il ticket è arrivato a Zurigo, avevamo già le seguenti informazioni:

  • Contenuto /etc/hosts
  • Contenuto /etc/resolv.conf
  • Conclusione iptables-save
  • Raccolto dal team ngrep file pcap

Con questi dati eravamo pronti a iniziare la fase di «indagine» e risoluzione dei problemi.

I nostri primi passi

Per prima cosa abbiamo controllato i log e lo stato del server dei metadati e ci siamo assicurati che funzionasse correttamente. Il server dei metadati risponde all'indirizzo IP 169.254.169.254 e, tra l'altro, si occupa del controllo dei nomi di dominio. Abbiamo anche ricontrollato che il firewall funzionasse correttamente con la VM e non bloccasse i pacchetti.

Era un problema piuttosto strano: il controllo di nmap ha smentito la nostra ipotesi principale sulla perdita dei pacchetti UDP, quindi abbiamo pensato ad altre opzioni e modi per verificarle:

  • I pacchetti scompaiono in modo selettivo? => Controllare le regole di iptables
  • Non è troppo piccolo MTU? => Проверить вывод ip a show
  • Il problema riguarda solo i pacchetti UDP o anche i TCP? => Eseguire dig +tcp
  • I pacchetti generati da dig ritornano? => Eseguire tcpdump
  • Funziona correttamente libdns? => Eseguire strace per verificare il passaggio dei pacchetti in entrambe le direzioni

Qui decidiamo di contattare l'utente per risolvere i problemi di persona.

Durante la chiamata riusciamo a controllare diverse cose:

  • Dopo alcune verifiche escludiamo le regole iptables dall'elenco delle cause.
  • Controlliamo le interfacce di rete e le tabelle di instradamento, e verifichiamo la correttezza dell'MTU.
  • Scopriamo che dig +tcp google.com (TCP) funziona come dovrebbe, mentre dig google.com (UDP) non funziona.
  • Eseguendo tcpdump finora funziona dig, scopriamo che i pacchetti UDP vengono restituiti.
  • Eseguiamo strace dig google.com e vediamo come dig chiama correttamente sendmsg() e recvms(),tuttavia il secondo viene interrotto per timeout.

Sfortunatamente, la fine del turno arriva e siamo costretti a trasferire il problema al prossimo fuso orario. Tuttavia, questa richiesta ha suscitato interesse nel nostro team, e un collega propone di creare un pacchetto DNS sorgente utilizzando il modulo Python scrapy.

from scapy.all import *

answer = sr1(IP(dst="169.254.169.254")/UDP(dport=53)/DNS(rd=1,qd=DNSQR(qname="google.com")),verbose=0)
print ("169.254.169.254", answer[DNS].summary())

Questo frammento crea un pacchetto DNS e invia la richiesta al server di metadati.

L'utente esegue il codice, la risposta DNS viene restituita e l'applicazione la riceve, confermando l'assenza di problemi a livello di rete.

Dopo un'altra 'discesa intorno al mondo', la richiesta torna al nostro team, e mi prendo completamente in carico la situazione, ritenendo che sia più comodo per l'utente se la richiesta smettesse di vagare.

Nel frattempo, l'utente gentilmente accetta di fornire un'immagine del sistema. Queste sono ottime notizie: avere la possibilità di testare il sistema da solo accelera significativamente la risoluzione dei problemi, poiché non devo più chiedere all'utente di eseguire comandi, inviarmi i risultati e analizzarli, posso fare tutto io!

I colleghi cominciano a provare un po' di invidia. Durante il pranzo discutiamo della richiesta, ma nessuno ha idee su cosa stia succedendo. Fortunatamente, lo stesso utente ha già preso misure per mitigare le conseguenze e non ha fretta, quindi abbiamo tempo per esaminare il problema. E poiché abbiamo l'immagine, possiamo eseguire tutti i test che ci interessano. Ottimo!

Tornando indietro di un passo,

una delle domande più comuni nei colloqui per la posizione di ingegnere di sistema è: 'Cosa succede quando pinghi www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Decido di applicare questa domanda tipica agli attuali problemi. In sintesi, quando cerchi di determinare un nome DNS, accade quanto segue:

  1. L'applicazione chiama la libreria di sistema, ad esempio libdns
  2. libdns controlla la configurazione del sistema riguardo a quale server DNS deve contattare (nella diagramma è 169.254.169.254, il server dei metadati)
  3. libdns utilizza chiamate di sistema per creare un socket UDP (SOCK_DGRAM) e inviare pacchetti UDP con la richiesta DNS in entrambe le direzioni
  4. Attraverso l'interfaccia sysctl è possibile configurare lo stack UDP a livello di kernel
  5. Il kernel interagisce con l'hardware per trasmettere pacchetti in rete attraverso l'interfaccia di rete
  6. L'ipervisor intercetta e invia il pacchetto al server dei metadati quando interagisce con esso
  7. Il server dei metadati, con la sua magia, determina il nome DNS e restituisce la risposta nello stesso modo

Storia dei pacchetti DNS scomparsi dal supporto tecnico di Google Cloud
Ricordo quali ipotesi abbiamo già considerato:

Ipotesi: le librerie sono danneggiate

  • Test 1: eseguire strace nel sistema, verificare che dig chiami le corrette chiamate di sistema
  • Risultato: vengono chiamate le corrette chiamate di sistema
  • Test 2: tramite srapy verificare se possiamo determinare i nomi evitando le librerie di sistema
  • Risultato: possiamo farlo
  • Test 3: eseguire rpm -V sul pacchetto libdns e md5sum sui file della libreria
  • Risultato: il codice della libreria è identico al codice nel sistema operativo funzionante
  • Test 4: montare un'immagine del sistema radice dell'utente su una VM senza questo comportamento, eseguire chroot, vedere se DNS funziona
  • Risultato: DNS funziona correttamente

Conclusione basata sui test: il problema non riguarda le librerie

Ipotesi: c'è un errore nelle impostazioni DNS

  • Test 1: controllare tcpdump e osservare se i pacchetti DNS vengono inviati e restituiti correttamente dopo aver eseguito dig
  • Risultato: i pacchetti vengono trasmessi correttamente
  • Test 2: ricontrollare sul server /etc/nsswitch.conf e /etc/resolv.conf
  • Risultato: tutto è corretto

Conclusione basata sui test: il problema non è nella configurazione DNS

Ipotesi: il kernel è danneggiato

  • Test: installare un nuovo kernel, verificare la firma, riavviare
  • Risultato: comportamento simile

Conclusione basata sui test: il kernel non è danneggiato

Ipotesi: comportamento errato della rete utente (o dell'interfaccia di rete dell'ipervisor)

  • Test 1: controllare le impostazioni del firewall
  • Risultato: il firewall consente il passaggio dei pacchetti DNS sia sull'host che su GCP
  • Test 2: intercettare il traffico e monitorare la correttezza della trasmissione e del rimando delle richieste DNS
  • Risultato: tcpdump conferma che l'host riceve i pacchetti di risposta

Conclusione basata sui test: il problema non è nella rete

Ipotesi: il server dei metadati non funziona

  • Test 1: controllare i log del server dei metadati per anomalie
  • Risultato: non ci sono anomalie nei log
  • Test 2: aggirare il server dei metadati tramite dig @8.8.8.8
  • Risultato: la risoluzione viene violata anche senza l'uso del server dei metadati

Conclusione basata sui test: il problema non è nel server dei metadati

Risultato: abbiamo testato tutti i sottosistemi tranne le impostazioni dell'ambiente di esecuzione!

Addentrandosi nelle impostazioni dell'ambiente di esecuzione del kernel

Per configurare l'ambiente di esecuzione del kernel, puoi utilizzare le opzioni della linea di comando (grub) oppure l'interfaccia sysctl. Ho dato un'occhiata a /etc/sysctl.conf e pensa un po', ho scoperto alcune impostazioni personalizzate. Sentendomi come se avessi afferrato qualcosa, ho scartato tutte le impostazioni non di rete o non tcp, rimanendo con un pugno di impostazioni net.core. Poi mi sono rivolto a dove sono le autorizzazioni host della VM e ho iniziato ad applicare una dopo l'altra le impostazioni dalla VM danneggiata, fino a trovare il colpevole:

net.core.rmem_default = 2147483647

Eccola, la configurazione DNS che rompe tutto! Ho trovato l'arma del delitto. Ma perché sta succedendo? Avevo ancora bisogno di un movente.

La configurazione della dimensione del buffer dei pacchetti DNS avviene tramite net.core.rmem_default. Un valore tipico varia attorno ai 200KiB, ma se il tuo server riceve molti pacchetti DNS, puoi aumentare la dimensione del buffer. Se al momento dell'arrivo di un nuovo pacchetto il buffer è pieno, ad esempio perché l'applicazione non lo elabora abbastanza rapidamente, inizierai a perdere pacchetti. Il nostro cliente ha correttamente aumentato la dimensione del buffer poiché temeva perdite di dati, dato che utilizzava un'applicazione di raccolta metriche attraverso pacchetti DNS. Il valore che ha impostato era il massimo possibile: 231-1 (impostare 231 restituisce

All'improvviso ho realizzato perché nmap e scapy funzionavano correttamente: utilizzavano socket raw! Gli socket raw sono diversi da quelli normali: lavorano aggirando iptables e non vengono bufferizzati!

Ma perché un 'buffer troppo grande' causa problemi? Chiaramente non funziona come dovrebbe.

A questo punto riuscivo a riprodurre il problema su diversi kernel e molte distribuzioni. Il problema già si manifestava sul kernel 3.x e ora si manifestava anche sul kernel 5.x.

Infatti, eseguendo

sysctl -w net.core.rmem_default=$((2**31-1))

DNS smetteva di funzionare.

Ho iniziato a cercare valori funzionanti attraverso un semplice algoritmo di ricerca binaria e ho scoperto che funzionava con 2147481343, ma per me quel numero era solo una serie insignificante di cifre. Ho suggerito al cliente di provare quel numero e mi ha risposto che il sistema ha funzionato con google.com, ma continuava a generare errori con altri domini, quindi ho proseguito le mie indagini.

Ho installato dropwatch, uno strumento che avrei dovuto usare molto tempo fa: mostra esattamente dove nel kernel arriva il pacchetto. La colpevole era la funzione udp_queue_rcv_skb. Ho scaricato i sorgenti del kernel e ho aggiunto alcune funzioni printk per tracciare esattamente dove finisce il pacchetto. Ho rapidamente scoperto la condizione necessaria if, e per un po' ho semplicemente fissato quella condizione, perché è proprio allora che tutto finalmente si è unito in un quadro coerente: 231-1, un numero insignificante, un dominio non funzionante… Il problema era in un pezzo di codice in __udp_enqueue_schedule_skb:

if (rmem > (size + sk->sk_rcvbuf))
		goto uncharge_drop;

Attenzione:

  • rmem è di tipo int
  • size è di tipo u16 (int non firmato a sedici bit) e memorizza la dimensione del pacchetto
  • sk->sk_rcybuf è di tipo int e memorizza la dimensione del buffer che per definizione è uguale al valore in net.core.rmem_default

Quando sk_rcvbuf si avvicina a 231, la somma della dimensione del pacchetto può portare a un overflow intero. E dato che è un int, il suo valore diventa negativo, quindi la condizione diventa vera quando dovrebbe essere falsa (puoi trovare maggiori informazioni su link).

L'errore viene corretto in modo triviale: effettuando un casting a unsigned int. Ho applicato la correzione e riavviato il sistema, dopo di che il DNS ha ripreso a funzionare.

Il gusto della vittoria

Ho inoltrato le mie scoperte al cliente e inviato LKML la patch del kernel. Sono soddisfatto: ogni pezzo del puzzle si è unito in un tutto unico, posso spiegare esattamente perché abbiamo osservato ciò che abbiamo osservato e, cosa più importante, abbiamo potuto trovare una soluzione al problema grazie al lavoro di squadra!

Va detto che il caso si è rivelato raro e, fortunatamente, riceviamo di rado richieste così complesse dagli utenti.

Storia dei pacchetti DNS scomparsi dal supporto tecnico di Google Cloud


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