Priča o nestalim DNS paketima iz tehničke podrške za Google Cloud

Iz Google uređivača blogova: Jeste li se ikada zapitali kako inženjeri Google Cloud Technical Solutions (TSE) rješavaju vaše zahtjeve za podršku? Inženjeri tehničke podrške za TSE odgovorni su za prepoznavanje i ispravljanje izvora problema koje su prijavili korisnici. Neki od ovih problema su prilično jednostavni, ali ponekad naiđete na kartu koja zahtijeva pažnju nekoliko inženjera odjednom. U ovom članku jedan od zaposlenika TSE-a ispričat će nam jedan vrlo škakljiv problem iz svoje nedavne prakse - slučaju nedostajućih DNS paketa. U ovoj priči vidjet ćemo kako su inženjeri uspjeli riješiti situaciju i što su novoga naučili ispravljajući grešku. Nadamo se da vas ova priča ne samo podučava o duboko ukorijenjenoj pogrešci, već vam daje i uvid u procese koji ulaze u podnošenje zahtjeva za podršku Google Cloudu.

Priča o nestalim DNS paketima iz tehničke podrške za Google Cloud

Rješavanje problema je i znanost i umjetnost. Sve počinje izgradnjom hipoteze o razlogu nestandardnog ponašanja sustava, nakon čega se testira njegova čvrstoća. Međutim, prije nego što formuliramo hipotezu, moramo jasno definirati i precizno formulirati problem. Ako pitanje zvuči previše nejasno, morat ćete sve pažljivo analizirati; Ovo je "umjetnost" rješavanja problema.

Pod Google Cloudom takvi procesi postaju eksponencijalno složeniji jer Google Cloud daje sve od sebe kako bi zajamčio privatnost svojih korisnika. Zbog toga inženjeri TSE-a nemaju pristup uređivanju vaših sustava, niti mogućnost pregleda konfiguracija tako široko kao korisnici. Stoga, da testiramo bilo koju od naših hipoteza, mi (inženjeri) ne možemo brzo modificirati sustav.

Neki korisnici vjeruju da ćemo mi sve popraviti poput mehaničara u autoservisu i jednostavno nam pošalju id virtualnog stroja, dok se u stvarnosti proces odvija u konverzacijskom formatu: prikupljanje informacija, formiranje i potvrđivanje (ili opovrgavanje) hipoteza, i, na kraju, problemi odluke temelje se na komunikaciji s klijentom.

Problem u pitanju

Danas imamo priču s dobrim završetkom. Jedan od razloga uspješnog rješavanja predloženog slučaja je vrlo detaljan i točan opis problema. Ispod možete vidjeti kopiju prve karte (uređenu da se sakriju povjerljivi podaci):
Priča o nestalim DNS paketima iz tehničke podrške za Google Cloud
Ova poruka sadrži mnogo korisnih informacija za nas:

  • Naveden je određeni VM
  • Sam problem je naznačen - DNS ne radi
  • Naznačeno je gdje se problem manifestira - VM i kontejner
  • Navedeni su koraci koje je korisnik poduzeo da identificira problem.

Zahtjev je evidentiran kao “P1: Kritičan utjecaj - usluga neupotrebljiva u produkciji”, što znači stalno praćenje situacije 24/7 prema shemi “Prati sunce” (više možete pročitati o prioritete korisničkih zahtjeva), s njegovim prijenosom s jednog tima tehničke podrške na drugi sa svakom promjenom vremenske zone. Zapravo, dok je problem stigao do našeg tima u Zürichu, već je obišao svijet. Do tada je korisnik poduzeo mjere ublažavanja, ali se bojao ponavljanja situacije u proizvodnji, budući da glavni uzrok još nije otkriven.

Kad je karta stigla u Zürich, već smo imali sljedeće informacije:

  • sadržaj /etc/hosts
  • sadržaj /etc/resolv.conf
  • Izlaz iptables-save
  • Sastavio tim ngrep pcap datoteka

S ovim podacima bili smo spremni započeti fazu "istraživanja" i rješavanja problema.

Naši prvi koraci

Prije svega, provjerili smo zapise i status poslužitelja metapodataka i uvjerili se da radi ispravno. Poslužitelj metapodataka odgovara na IP adresu 169.254.169.254 i, između ostalog, odgovoran je za kontrolu imena domena. Također smo još jednom provjerili radi li vatrozid ispravno s VM-om i ne blokira li pakete.

Bio je to neka vrsta čudnog problema: nmap provjera opovrgla je našu glavnu hipotezu o gubitku UDP paketa, pa smo mentalno smislili još nekoliko opcija i načina da ih provjerimo:

  • Odbacuju li se paketi selektivno? => Provjerite iptables pravila
  • Nije li premalen? MTU? => Provjerite izlaz ip a show
  • Utječe li problem samo na UDP pakete ili i na TCP? => Odvezite se dig +tcp
  • Vraćaju li se paketi generirani dig-om? => Odvezite se tcpdump
  • Radi li libdns ispravno? => Odvezite se strace za provjeru prijenosa paketa u oba smjera

Ovdje odlučujemo pozvati korisnika kako bismo uživo riješili probleme.

Tijekom razgovora u mogućnosti smo provjeriti nekoliko stvari:

  • Nakon nekoliko provjera isključujemo iptables pravila s popisa razloga
  • Provjeravamo mrežna sučelja i tablice usmjeravanja te još jednom provjeravamo je li MTU točan
  • To otkrivamo dig +tcp google.com (TCP) radi kako treba, ali dig google.com (UDP) ne radi
  • Odvezavši se tcpdump još uvijek radi dig, nalazimo da se UDP paketi vraćaju
  • Odvezemo se strace dig google.com i vidimo kako dig ispravno poziva sendmsg() и recvms(), međutim drugi je prekinut timeoutom

Nažalost, dolazi kraj smjene i prisiljeni smo eskalirati problem na sljedeću vremensku zonu. Zahtjev je, međutim, izazvao interes u našem timu i kolega predlaže izradu početnog DNS paketa pomoću scrapy Python modula.

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())

Ovaj fragment stvara DNS paket i šalje zahtjev poslužitelju metapodataka.

Korisnik pokreće kod, DNS odgovor se vraća, a aplikacija ga prima, potvrđujući da nema problema na mrežnoj razini.

Nakon još jednog "putovanja oko svijeta", zahtjev se vraća našem timu, a ja ga u potpunosti prenosim sebi, misleći da će korisniku biti zgodnije ako zahtjev prestane kružiti s mjesta na mjesto.

U međuvremenu, korisnik ljubazno pristaje dati snimku slike sustava. Ovo je jako dobra vijest: mogućnost da sam testiram sustav čini rješavanje problema mnogo bržim, jer više ne moram tražiti od korisnika da pokreće naredbe, šalje mi rezultate i analizira ih, sve mogu sam!

Kolege mi počinju pomalo zavidjeti. Za ručkom razgovaramo o pretvorbi, ali nitko nema pojma što se događa. Srećom, sam korisnik je već poduzeo mjere za ublažavanje posljedica i ne žuri mu se, tako da imamo vremena secirati problem. A budući da imamo sliku, možemo pokrenuti sve testove koji nas zanimaju. Sjajno!

Korak unatrag

Jedno od najpopularnijih pitanja na intervjuu za pozicije inženjera sustava je: “Što se događa kada pingate www.google.com? Pitanje je super, budući da kandidat treba opisati sve, od ljuske do korisničkog prostora, do jezgre sustava pa sve do mreže. Smiješim se: ponekad se pitanja za intervju pokažu korisnima u stvarnom životu...

Odlučio sam primijeniti ovo HR pitanje na trenutačni problem. Grubo govoreći, kada pokušate odrediti DNS naziv, događa se sljedeće:

  1. Aplikacija poziva sistemsku biblioteku kao što je libdns
  2. libdns provjerava konfiguraciju sustava na koji DNS poslužitelj treba kontaktirati (na dijagramu je to 169.254.169.254, poslužitelj metapodataka)
  3. libdns koristi sistemske pozive za stvaranje UDP utičnice (SOKET_DGRAM) i slanje UDP paketa s DNS upitom u oba smjera
  4. Preko sysctl sučelja možete konfigurirati UDP stog na razini kernela
  5. Kernel je u interakciji s hardverom za prijenos paketa preko mreže putem mrežnog sučelja
  6. Hipervizor hvata i šalje paket poslužitelju metapodataka nakon kontakta s njim
  7. Poslužitelj metapodataka, svojom magijom, određuje DNS ime i vraća odgovor koristeći istu metodu

Priča o nestalim DNS paketima iz tehničke podrške za Google Cloud
Dopustite mi da vas podsjetim koje smo hipoteze već razmotrili:

Hipoteza: neispravne knjižnice

  • Test 1: pokrenite strace u sustavu, provjerite poziva li dig ispravne sistemske pozive
  • Rezultat: Pozivaju se ispravni sistemski pozivi
  • Test 2: korištenje srapy za provjeru možemo li odrediti imena zaobilazeći sistemske biblioteke
  • Rezultat: možemo
  • Test 3: pokrenite rpm –V na paketu libdns i datotekama knjižnice md5sum
  • Rezultat: kod knjižnice potpuno je identičan kodu operativnog sustava
  • Test 4: montirajte sliku korijenskog sustava korisnika na VM bez ovog ponašanja, pokrenite chroot, pogledajte radi li DNS
  • Rezultat: DNS radi ispravno

Zaključak na temelju testova: problem nije u knjižnicama

Hipoteza: Postoji greška u postavkama DNS-a

  • Test 1: provjerite tcpdump i vidite šalju li se DNS paketi i vraćaju ispravno nakon pokretanja dig-a
  • Rezultat: paketi se prenose ispravno
  • Test 2: dva puta provjerite na poslužitelju /etc/nsswitch.conf и /etc/resolv.conf
  • Rezultat: sve je ispravno

Zaključak na temelju testova: problem nije u DNS konfiguraciji

Hipoteza: oštećena jezgra

  • Test: instalirajte novi kernel, provjerite potpis, ponovno pokrenite
  • Rezultat: slično ponašanje

Zaključak na temelju testova: kernel nije oštećen

Hipoteza: neispravno ponašanje korisničke mreže (ili mrežnog sučelja hipervizora)

  • Test 1: Provjerite postavke vatrozida
  • Rezultat: vatrozid propušta DNS pakete i na glavnom računalu i na GCP-u
  • Test 2: presretanje prometa i praćenje ispravnosti prijenosa i vraćanja DNS zahtjeva
  • Rezultat: tcpdump potvrđuje da je host primio povratne pakete

Zaključak na temelju testova: problem nije u mreži

Hipoteza: poslužitelj metapodataka ne radi

  • Test 1: provjerite zapise poslužitelja metapodataka za anomalije
  • Rezultat: nema anomalija u zapisima
  • Test 2: Zaobiđite poslužitelj metapodataka putem dig @8.8.8.8
  • Rezultat: Razlučivost je pokvarena čak i bez korištenja poslužitelja metapodataka

Zaključak na temelju testova: problem nije u poslužitelju metapodataka

Dno crta: testirali smo sve podsustave osim postavke vremena izvođenja!

Zaronite u postavke vremena izvođenja kernela

Za konfiguriranje okruženja za izvršavanje kernela, možete koristiti opcije naredbenog retka (grub) ili sučelje sysctl. Pogledao sam u /etc/sysctl.conf i samo razmislite, otkrio sam nekoliko prilagođenih postavki. Osjećajući se kao da sam se nečega uhvatio, odbacio sam sve ne-mrežne ili ne-tcp postavke, ostajući na planinskim postavkama net.core. Zatim sam otišao do mjesta gdje su bile dozvole za host u VM-u i počeo primjenjivati ​​postavke jednu po jednu, jednu za drugom, s pokvarenim VM-om, sve dok nisam pronašao krivca:

net.core.rmem_default = 2147483647

Evo ga, konfiguracija za razbijanje DNS-a! Našao sam oružje kojim je izvršeno ubojstvo. Ali zašto se to događa? Još mi je trebao motiv.

Osnovna veličina međuspremnika DNS paketa konfigurirana je putem net.core.rmem_default. Tipična vrijednost je negdje oko 200KiB, ali ako vaš poslužitelj prima mnogo DNS paketa, možda biste trebali povećati veličinu međuspremnika. Ako je međuspremnik pun kada stigne novi paket, na primjer zato što ga aplikacija ne obrađuje dovoljno brzo, tada ćete početi gubiti pakete. Naš klijent je ispravno povećao veličinu međuspremnika jer se bojao gubitka podataka, jer je koristio aplikaciju za prikupljanje metrike putem DNS paketa. Vrijednost koju je postavio bila je najveća moguća: 231-1 (ako je postavljena na 231, kernel će vratiti "INVALID ARGUMENT").

Odjednom sam shvatio zašto nmap i scapy rade ispravno: koristili su sirove utičnice! Neobrađeni soketi se razlikuju od običnih soketa: oni zaobilaze iptables i nisu baferirani!

Ali zašto "buffer prevelik" uzrokuje probleme? Očito ne radi kako je zamišljeno.

U ovom trenutku mogao bih reproducirati problem na više kernela i više distribucija. Problem se već pojavio na 3.x kernelu, a sada se pojavio i na 5.x kernelu.

Dapače, pri pokretanju

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

DNS je prestao raditi.

Počeo sam tražiti radne vrijednosti pomoću jednostavnog algoritma binarnog pretraživanja i otkrio da sustav radi s 2147481343, ali ovaj broj za mene je bio besmislen skup brojeva. Predložio sam klijentu da pokuša s ovim brojem, a on je odgovorio da sustav radi s google.com, ali i dalje daje pogrešku s drugim domenama, pa sam nastavio istragu.

Instalirao sam padalica, alat koji je trebao biti korišten ranije: pokazuje točno gdje u kernelu paket završava. Krivac je bila funkcija udp_queue_rcv_skb. Preuzeo sam izvore kernela i dodao nekoliko funkcija printk kako bi pratili gdje točno paket završava. Brzo sam pronašao pravo stanje if, i jednostavno ga gledao neko vrijeme, jer tada se konačno sve sklopilo u jednu cjelovitu sliku: 231-1, besmislen broj, neradna domena... Bio je to dio koda u __udp_enqueue_schedule_skb:

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

Imajte na umu:

  • rmem je tipa int
  • size je tipa u16 (bez predznaka šesnaestobitni int) i pohranjuje veličinu paketa
  • sk->sk_rcybuf je tipa int i pohranjuje veličinu međuspremnika koja je, po definiciji, jednaka vrijednosti u net.core.rmem_default

Kada sk_rcvbuf približava 231, zbrajanje veličine paketa može rezultirati cjelobrojno prekoračenje. A budući da je to int, njegova vrijednost postaje negativna, pa uvjet postaje istinit kada bi trebao biti lažan (više o tome možete pročitati na link).

Greška se može ispraviti na trivijalan način: lijevanjem unsigned int. Primijenio sam popravak i ponovno pokrenuo sustav i DNS je ponovno radio.

Okus pobjede

Svoje sam nalaze proslijedio naručitelju i poslao LKML kernel patch. Zadovoljan sam: svaki dio slagalice se slaže, mogu točno objasniti zašto smo promatrali to što smo promatrali, i što je najvažnije, uspjeli smo pronaći rješenje problema zahvaljujući našem timskom radu!

Vrijedno je priznati da se slučaj pokazao rijetkim, a srećom rijetko primamo ovako složene zahtjeve korisnika.

Priča o nestalim DNS paketima iz tehničke podrške za Google Cloud


Izvor: www.habr.com

Dodajte komentar