Priča o nedostajućim DNS paketima iz Google Cloud tehničke podrške

Iz Google Blog Editora: Jeste li se ikada zapitali kako inženjeri Google Cloud Technical Solutions (TSE) rješavaju vaše zahtjeve za podršku? TSE inženjeri tehničke podrške odgovorni su za identifikaciju 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 zaposlenih u TSE-u će nam ispričati jedan veoma škakljiv problem iz svoje nedavne prakse - slučaj da nedostaju DNS paketi. U ovoj priči ćemo vidjeti kako su inženjeri uspjeli riješiti nastalu situaciju, te šta su novo naučili dok su otklanjali grešku. Nadamo se da vas ova priča ne samo obrazuje o duboko ukorijenjenoj grešci, već vam daje i uvid u procese koji se odnose na podnošenje zahtjeva za podršku u Google Cloud.

Priča o nedostajućim DNS paketima iz Google Cloud tehničke podrške

Rješavanje problema je i nauka i umjetnost. Sve počinje izgradnjom hipoteze o razlogu nestandardnog ponašanja sistema, nakon čega se testira na snagu. Međutim, prije nego što formulišemo hipotezu, moramo jasno definirati i precizno formulirati problem. Ako pitanje zvuči previše nejasno, onda ćete morati sve pažljivo analizirati; Ovo je "umetnost" rešavanja problema.

Pod Google Cloud-om, takvi procesi postaju eksponencijalno složeniji, jer Google Cloud pokušava da garantuje privatnost svojih korisnika. Zbog toga, TSE inženjeri nemaju pristup uređivanju vaših sistema, niti mogućnost pregleda konfiguracija tako široko kao korisnici. Stoga, da bismo testirali bilo koju od naših hipoteza, mi (inženjeri) ne možemo brzo modificirati sistem.

Neki korisnici vjeruju da ćemo sve popraviti kao mehaniku u autoservisu, i jednostavno nam poslati id virtuelne mašine, dok se u stvarnosti proces odvija u konverzacijskom formatu: prikupljanje informacija, formiranje i potvrđivanje (ili pobijanje) hipoteza, i, na kraju, problemi odlučivanja se zasnivaju na komunikaciji sa klijentom.

Problem je u pitanju

Danas imamo priču sa dobrim završetkom. Jedan od razloga za uspješno rješavanje predloženog slučaja je vrlo detaljan i tačan opis problema. Ispod možete vidjeti kopiju prve ulaznice (uređene da sakriju povjerljive informacije):
Priča o nedostajućim DNS paketima iz Google Cloud tehničke podrške
Ova poruka sadrži mnogo korisnih informacija za nas:

  • Specifičan VM je specificiran
  • Sam problem je naznačen - DNS ne radi
  • Naznačeno je gdje se problem manifestira - VM i kontejner
  • Naznačeni su koraci koje je korisnik poduzeo da identifikuje problem.

Zahtjev je zaveden kao “P1: Kritičan uticaj - usluga neupotrebljiva u proizvodnji”, što znači stalno praćenje situacije 24/7 po šemi “Prati sunce” (više o prioriteti korisničkih zahtjeva), sa njegovim prijenosom iz jednog tima tehničke podrške u drugi sa svakim pomjeranjem vremenske zone. Zapravo, dok je problem stigao do našeg tima u Cirihu, već je obišao svijet. Do tog trenutka korisnik je poduzeo mjere ublažavanja, ali se bojao ponavljanja situacije u proizvodnji, jer uzrok još nije otkriven.

Dok je karta stigla u Cirih, već smo imali pri ruci sljedeće informacije:

  • Sadržaj /etc/hosts
  • Sadržaj /etc/resolv.conf
  • zaključak iptables-save
  • Sastavio tim ngrep pcap fajl

Sa ovim podacima bili smo spremni da započnemo fazu “istrage” i otklanjanja problema.

Naši prvi koraci

Prije svega, provjerili smo logove i status servera metapodataka i uvjerili se da radi ispravno. Server metapodataka odgovara na IP adresu 169.254.169.254 i, između ostalog, odgovoran je za kontrolu imena domena. Također smo dvaput provjerili da li firewall radi ispravno sa VM-om i da ne blokira pakete.

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

  • Da li se paketi ispuštaju selektivno? => Provjerite iptables pravila
  • Nije li premalo? MTU? => Provjerite izlaz ip a show
  • Da li problem utiče samo na UDP pakete ili TCP? => Odvezite se dig +tcp
  • Da li se vraćaju dig generisani paketi? => Odvezite se tcpdump
  • Da li libdns radi ispravno? => Odvezite se strace za provjeru prijenosa paketa u oba smjera

Ovdje odlučujemo da pozovemo korisnika za rješavanje problema uživo.

Tokom poziva u mogućnosti smo provjeriti nekoliko stvari:

  • Nakon nekoliko provjera isključujemo iptables pravila sa liste razloga
  • Provjeravamo mrežna sučelja i tablice rutiranja i još jednom provjeravamo da li je MTU ispravan
  • Otkrivamo to 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 vraćaju UDP paketi
  • Odvezemo se strace dig google.com i vidimo kako dig ispravno zove sendmsg() и recvms(), međutim, drugi je prekinut timeoutom

Nažalost, dolazi kraj smjene i primorani smo eskalirati problem na sljedeću vremensku zonu. Zahtjev je, međutim, izazvao interesovanje u našem timu, pa kolega predlaže kreiranje početnog DNS paketa pomoću skrapy 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 kreira DNS paket i šalje zahtjev serveru metapodataka.

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

Nakon još jednog „putovanja oko svijeta“, zahtjev se vraća našem timu, a ja ga potpuno prenosim na sebe, misleći da će korisniku biti zgodnije ako zahtjev prestane da kruži od mjesta do mjesta.

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

Moje kolege počinju pomalo da mi zavide. Za ručkom razgovaramo o konverziji, ali niko nema pojma šta se dešava. Na sreću, sam korisnik je već poduzeo mjere za ublažavanje posljedica i ne žuri, tako da imamo vremena da seciramo problem. A pošto imamo sliku, možemo pokrenuti sve testove koji nas zanimaju. Odlično!

Pravi korak unazad

Jedno od najpopularnijih pitanja za intervju za pozicije sistemskog inženjera je: „Šta se dešava kada pingujete www.google.com? Pitanje je odlično, jer kandidat treba da opiše sve od ljuske do korisničkog prostora, do sistemskog kernela i zatim do mreže. Smiješim se: ponekad se pitanja za intervju pokažu korisnima u stvarnom životu...

Odlučujem primijeniti ovo HR pitanje na trenutni problem. Grubo govoreći, kada pokušate da odredite DNS ime, dešava se sledeće:

  1. Aplikacija poziva sistemsku biblioteku kao što je libdns
  2. libdns provjerava konfiguraciju sistema sa kojim DNS serverom treba da kontaktira (na dijagramu je ovo 169.254.169.254, server metapodataka)
  3. libdns koristi sistemske pozive za kreiranje UDP utičnice (SOKET_DGRAM) i slanje UDP paketa sa DNS upitom u oba smjera
  4. Preko sysctl interfejsa možete konfigurisati UDP stek na nivou kernela
  5. Kernel stupa u interakciju s hardverom za prijenos paketa preko mreže preko mrežnog sučelja
  6. Hipervizor hvata i prenosi paket na server metapodataka nakon kontakta s njim
  7. Server metapodataka, svojom magijom, određuje DNS ime i vraća odgovor koristeći isti metod

Priča o nedostajućim DNS paketima iz Google Cloud tehničke podrške
Dozvolite mi da vas podsjetim koje smo hipoteze već razmatrali:

Hipoteza: Pokvarene biblioteke

  • Test 1: pokrenite strace u sistemu, provjerite da li dig poziva ispravne sistemske pozive
  • Rezultat: Pozivaju se ispravni sistemski pozivi
  • Test 2: pomoću srapy-a provjerimo možemo li odrediti imena zaobilazeći sistemske biblioteke
  • Rezultat: možemo
  • Test 3: pokrenite rpm –V na libdns paketu i datotekama biblioteke md5sum
  • Rezultat: kod biblioteke je potpuno identičan kodu u operativnom sistemu koji radi
  • Test 4: montirajte korisničku sliku root sistema na VM bez ovog ponašanja, pokrenite chroot, pogledajte radi li DNS
  • Rezultat: DNS radi ispravno

Zaključak na osnovu testova: problem nije u bibliotekama

Hipoteza: Postoji greška u postavkama DNS-a

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

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

Hipoteza: oštećeno jezgro

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

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

Hipoteza: pogrešno ponašanje korisničke mreže (ili hipervizorskog mrežnog interfejsa)

  • Test 1: Provjerite postavke zaštitnog zida
  • Rezultat: firewall prosljeđuje DNS pakete i na hostu i na GCP
  • Test 2: presretanje saobraćaja i praćenje ispravnosti prenosa i vraćanja DNS zahteva
  • Rezultat: tcpdump potvrđuje da je host primio povratne pakete

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

Hipoteza: server metapodataka ne radi

  • Test 1: provjerite dnevnike servera metapodataka da li postoje anomalije
  • Rezultat: nema anomalija u evidenciji
  • Test 2: Zaobiđite server metapodataka preko dig @8.8.8.8
  • Rezultat: Rezolucija je narušena čak i bez korištenja servera metapodataka

Zaključak na osnovu testova: problem nije u serveru metapodataka

Bottom line: testirali smo sve podsisteme osim postavke vremena izvođenja!

Zaroniti u postavke kernela runtime

Da biste konfigurisali okruženje za izvršavanje kernela, možete koristiti opcije komandne linije (grub) ili sysctl interfejs. Pogledao sam unutra /etc/sysctl.conf i samo pomislite, otkrio sam nekoliko prilagođenih postavki. Osjećajući se kao da sam se uhvatio za nešto, odbacio sam sve postavke koje nisu mrežne ili ne-tcp, ostajući pri planinskim postavkama net.core. Zatim sam otišao do mjesta gdje su dozvole hosta bile u VM-u i počeo primjenjivati ​​postavke jednu po jednu, jednu za drugom, sa pokvarenim VM-om, dok nisam pronašao krivca:

net.core.rmem_default = 2147483647

Evo je, konfiguracija za razbijanje DNS-a! Našao sam oružje ubistva. Ali zašto se to dešava? I dalje mi je trebao motiv.

Osnovna veličina bafera DNS paketa se konfiguriše putem net.core.rmem_default. Tipična vrijednost je negdje oko 200KiB, ali ako vaš server prima mnogo DNS paketa, možda ćete htjeti povećati veličinu bafera. Ako je bafer 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 bafera jer se bojao gubitka podataka, jer je koristio aplikaciju za prikupljanje metrike preko DNS paketa. Vrijednost koju je postavio bila je maksimalna moguća: 231-1 (ako je postavljeno na 231, kernel će vratiti “INVALID ARGUMENT”).

Odjednom sam shvatio zašto nmap i scapy rade ispravno: koristili su sirove utičnice! Sirovi utičnici se razlikuju od običnih utičnica: zaobilaze iptables i nisu baferovani!

Ali zašto "bafer prevelik" uzrokuje probleme? Očigledno 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 kernelu 3.x, a sada se pojavio i na kernelu 5.x.

Zaista, nakon pokretanja

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

DNS je prestao da radi.

Počeo sam tražiti radne vrijednosti kroz jednostavan algoritam binarnog pretraživanja i otkrio da sistem radi sa 2147481343, ali ovaj broj je za mene bio besmislen skup brojeva. Predložio sam klijentu da proba ovaj broj, a on je odgovorio da sistem radi sa google.com, ali je ipak dao grešku sa drugim domenima, pa sam nastavio istragu.

Instalirao sam dropwatch, alat koji je trebao biti korišten ranije: pokazuje gdje tačno u kernelu završava paket. Krivac je bila funkcija udp_queue_rcv_skb. Skinuo sam izvore kernela i dodao nekoliko funkcije printk za praćenje gdje tačno paket završava. Brzo sam pronašao pravo stanje if, i jednostavno zurio u to neko vrijeme, jer se tada sve konačno spojilo u jednu sliku: 231-1, besmislen broj, neradni domen... 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 (nepotpisan šesnaestobitni int) i pohranjuje veličinu paketa
  • sk->sk_rcybuf je tipa int i pohranjuje veličinu bafera koja je, po definiciji, jednaka vrijednosti u net.core.rmem_default

Kada sk_rcvbuf približava se 231, sabiranje veličine paketa može rezultirati prekoračenje cijelog broja. A pošto je int, njegova vrijednost postaje negativna, tako da 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: ubacivanjem unsigned int. Primijenio sam ispravku i ponovo pokrenuo sistem i DNS je ponovo radio.

Okus pobjede

Svoje nalaze sam prosledio klijentu i poslao LKML kernel patch. Zadovoljan sam: svaki deo slagalice se uklapa, mogu tačno da objasnim zašto smo primetili ono što smo primetili, i što je najvažnije, uspeli smo da pronađemo rešenje problema zahvaljujući našem timskom radu!

Vrijedi priznati da se slučaj pokazao rijetkim, a na sreću rijetko primamo ovako složene zahtjeve korisnika.

Priča o nedostajućim DNS paketima iz Google Cloud tehničke podrške


izvor: www.habr.com

Dodajte komentar