Priča o nestalim DNS paketima od podrške Google Clouda

Od urednika Google bloga: Jeste li se ikada zapitali kako inženjeri Google Cloud tehničkih rješenja (TSE) rukovode vašim zahtjevima za podršku? Na inženjerima tehničke podrške TSE je odgovornost otkrivanje i rješavanje problema koje su korisnici prijavili. Neki od tih problema su prilično jednostavni, ali ponekad naiđemo na zahtjev koji zahtijeva pažnju nekoliko inženjera. U ovom članku jedan od zaposlenika TSE-a ispričat će nam o jednom vrlo složenom problemu iz svoje nedavne prakse — slučaj s nestajanjem DNS paketa. Tokom ove priče vidjet ćemo na koji su način inženjeri uspjeli riješiti situaciju i što su novo naučili tijekom rješavanja greške. Nadamo se da će ova priča ne samo otkriti duboko usađenu grešku, nego također pružiti uvid u procese koji se odvijaju prilikom podnošenja zahtjeva za podršku Google Cloudu.

Priča o nestalim DNS paketima od podrške Google Clouda

Otklanjanje grešaka je istovremeno i znanost i umjetnost. Sve započinje izradom hipoteze o uzroku neobičnog ponašanja sustava, nakon čega se ona testira. Međutim, prije nego što formiramo hipotezu, moramo jasno definirati i precizno formulirati problem. Ako pitanje zvuči previše nejasno, morat ćete ga pažljivo analizirati; tu dolazi do izražaja „umjetnost“ otklanjanja grešaka.

U uvjetima Google Clouda, takvi se procesi višestruko kompliciraju, jer Google Cloud nastoji osigurati privatnost svojih korisnika. Zbog toga inženjeri TSE nemaju pristup uređivanju vaših sustava niti mogućnost tako širokog pregledavanja konfiguracija kao što to rade korisnici. Stoga, za provjeru bilo koje od naših hipoteza, (inženjeri) ne možemo brzo modificirati sustav.

Neki korisnici misle da ćemo sve ispraviti poput mehaničara u servisu, i jednostavno nam šalju ID virtualne mašine, dok u stvarnosti proces izgleda kao razgovor: prikupljanje informacija, izrada i potvrda (ili opovrgavanje) hipoteza, a na kraju se rješenje problema temelji na komunikaciji s klijentom.

Proučavani problem

Danas imamo priču s sretnim završetkom. Jedan od razloga uspješnog rješavanja predloženog slučaja leži u vrlo detaljnom i točnom opisu problema. Ispod možete vidjeti kopiju prvog tiketa (uređenog kako bi se sakrila povjerljiva informacija):
Priča o nestalim DNS paketima od podrške Google Clouda
U ovoj poruci ima puno korisnih informacija za nas:

  • Navedena je konkretna VM
  • Navedeni je sam problem — ne radi DNS
  • Navedeno je gdje se problem pojavljuje — VM i kontejner
  • Navedeni su koraci koje je korisnik poduzeo kako bi identificirao problem

Poziv je registriran kao «P1: Kritični utjecaj — Usluga neupotrebljiva u produkciji», što znači stalno praćenje situacije 24/7 prema shemi «Follow the Sun» (ovdje možete pročitati više o prioritetima korisničkih poziva), s prijenosom od jednog tima tehničke podrške do drugog pri svakom pomaku vremenskih zona. U suštini, do trenutka kada je problem stigao do našeg tima u Zürichem, uspio je obići cijeli svijet. U to vrijeme korisnik je poduzeo mjere za smanjenje posljedica, no bojao se ponovnog događaja u produkciji, jer osnovni uzrok još uvijek nije bio otkriven.

U trenutku kada je tiket stigao u Zürich, već smo imali sljedeće informacije:

  • Sadržaj /etc/hosts
  • Sadržaj /etc/resolv.conf
  • Izvještaj iptables-save
  • Prikupio tim ngrep pcap datoteku

S ovim podacima bili smo spremni započeti fazu «istrage» i otklanjanja poteškoća.

Naši prvi koraci

Prvo smo provjerili logove i status servera metapodataka te se uvjerili da ispravno radi. Server metapodataka odgovara na IP adresu 169.254.169.254 i, osim toga, odgovara za kontrolu nad nazivima domena. Također smo provjerili da vatrozid ispravno radi s VM i da ne blokira pakete.

To je bio neki čudan problem: provjera nmap je opovrgla našu osnovnu hipotezu o gubitku UDP paketa, stoga smo mentalno iznijeli još nekoliko opcija i načina provjere:

  • Gube li se paketi selektivno? => Provjeriti pravila iptables
  • Je li možda previše mali MTU? => Проверить вывод ip a show
  • Utječe li problem samo na UDP pakete ili i na TCP? => Provesti dig +tcp
  • Vraćaju li se generirani dig paketi? => Provesti tcpdump
  • Radi li ispravno libdns? => Provesti strace za provjeru prijenosa paketa u oba smjera

Ovdje odlučujemo da se spojimo s korisnikom kako bismo otklonili poteškoće uživo.

Tijekom poziva uspijevamo provjeriti nekoliko stvari:

  • Nakon nekoliko provjera isključujemo pravila iptables iz popisa mogućih uzroka
  • Provjeravamo mrežne sučelje i tablice usmjeravanja, te ponovno provjeravamo ispravnost MTU-a
  • Otkrivamo da dig +tcp google.com (TCP) radi kako treba, ali dig google.com (UDP) ne radi
  • Pokrenuvši tcpdump trenutno radi dig, otkrivamo da UDP paketi dolaze natrag
  • Pokrećemo strace dig google.com i vidimo kako dig ispravno poziva sendmsg() i recvms(), međutim, drugi se prekida zbog isteka vremena

Nažalost, smjena se završava i prisiljeni smo prenijeti problem u sljedeću vremensku zonu. Ipak, zahtjev je pobudio interes u našem timu, a kolega predlaže izradu izvornih DNS paketa pomoću Python modula 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())

Ovaj fragment stvara DNS paket i šalje upit serveru metapodataka.

Korisnik pokreće kod, DNS odgovor se vraća, a aplikacija ga prima, što potvrđuje odsutnost problema na razini mreže.

Nakon još jednog "putovanja oko svijeta" zahtjev se vraća našem timu, i potpuno ga preuzimam, smatrajući da će korisniku biti lakše ako zahtjev prestane kružiti naokolo.

U međuvremenu, korisnik ljubazno pristaje pružiti snimku slike sustava. Ovo su vrlo dobre vijesti: mogućnost samostalnog testiranja sustava znatno ubrzava rješavanje problema, jer više ne trebam tražiti od korisnika da pokreće naredbe, šalje mi rezultate i analizira ih, sve mogu učiniti sam!

Kolege mi pomalo zavide. Za ručkom raspravljamo o zahtjevu, no nitko nema ideja o tome što se događa. Srećom, sam korisnik je već poduzeo mjere za ublažavanje posljedica i nema se gdje žuriti, tako da imamo vremena razraditi problem. A budući da imamo sliku, možemo provesti sve testove koji nas zanimaju. Odlično!

Vraćajući se korak unatrag

Jedno od najpopularnijih pitanja na intervjuima za posao sistemskog inženjera glasi: "Što se događa kada pingate www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Odlazim primijeniti ovo HR pitanje na trenutni problem. Grubo rečeno, kada pokušavate odrediti DNS ime, događa se sljedeće:

  1. Aplikacija poziva sistemsku biblioteku, na primjer libdns
  2. libdns provjerava konfiguraciju sustava kojem DNS serveru se treba obratiti (na dijagramu to je 169.254.169.254, server metapodataka)
  3. libdns koristi sistemske pozive za stvaranje UDP soketa (SOCK_DGRAM) i slanje UDP paketa s DNS upitom u obje strane
  4. Kroz sučelje sysctl može se konfigurirati UDP stog na razini jezgre
  5. Jezgra komunicira s hardverom za prijenos paketa putem mrežnog sučelja
  6. Hipervizor hvata i proslijeđuje paket serveru metapodataka kada dođe do kontakta
  7. Server metapodataka svojim čarobnjaštvom utvrđuje DNS ime i na isti način vraća odgovor

Priča o nestalim DNS paketima od podrške Google Clouda
Podsjećam, koje smo hipoteze već razmotrili:

Hipoteza: Biblioteke su oštećene

  • Test 1: pokrenuti strace u sustavu, provjeriti je li dig poziva ispravne sistemske pozive
  • Rezultat: pozivaju se ispravni sistemi pozivi
  • Test 2: putem srapy provjeriti možemo li određivati imena zaobilazeći sistemske biblioteke
  • Rezultat: možemo
  • Test 3: pokrenuti rpm –V na paketu libdns i md5sum datoteka biblioteke
  • Rezultat: kod biblioteke je potpuno identičan kodu u radnom operativnom sustavu
  • Test 4: montirati sliku korijenskog sustava korisnika na VM bez sličnog ponašanja, pokrenuti chroot, vidjeti radi li DNS
  • Rezultat: DNS radi ispravno

Zaključak na temelju testova: problem nije u bibliotekama

Hipoteza: Prisutna je greška u DNS konfiguraciji

  • Test 1: provjeriti tcpdump i promatrati ispravno li se šalju i primaju DNS paketi nakon pokretanja dig
  • Rezultat: paketi se ispravno prenose
  • Test 2: ponovo provjeriti na serveru /etc/nsswitch.conf i /etc/resolv.conf
  • Rezultat: sve je ispravno

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

Hipoteza: jezgra je oštećena

  • Test: instalirati novu jezgru, provjeriti potpis, ponovno pokrenuti
  • Rezultat: isto ponašanje

Zaključak na temelju testova: jezgra nije oštećena

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

  • Test 1: provjeriti postavke vatrozida
  • Rezultat: vatrozid propušta DNS pakete kako na hostu, tako i na GCP-u
  • Test 2: presresti promet i pratiti ispravnost prijenosa i povratka DNS upita
  • Rezultat: tcpdump potvrđuje primitak povratnih paketa od strane hosta

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

Hipoteza: server metapodataka ne radi

  • Test 1: provjeriti logove servera metapodataka na anomalije
  • Rezultat: u logovima nema anomalija
  • Test 2: zaobići poslužitelj metapodataka preko dig @8.8.8.8
  • Rezultat: razrješavanje se prekida čak i bez korištenja poslužitelja metapodataka

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

Zaključak: testirali smo sve podsustave osim postavki okruženja izvršenja!

Zalazeći u postavke okruženja izvršenja jezgre

Za postavljanje okruženja izvršenja jezgre možete iskoristiti opcije naredbenog retka (grub) ili sučelje sysctl. Zavirio sam u /etc/sysctl.conf i pomislite, otkrio sam nekoliko prilagođenih postavki. Imaginirajući da sam zgrabil nešto, odbacio sam sve non-network ili non-tcp postavke, ostajući s hrpom postavki net.core. Zatim sam se obratio onome gdje su VM smještene dozvole domaćina i počeo primjenjivati jednu za drugom postavke s pokvarenom VM, sve dok nisam došao do počinitelja:

net.core.rmem_default = 2147483647

Evo ga, razbijač DNS konfiguracije! Pronašao sam oružje zločina. No, zašto se to događa? Još uvijek mi je trebao motiv.

Postavljanje osnovne veličine DNS paketa odvija se kroz net.core.rmem_default. Tipična vrijednost varira negdje između 200KiB, međutim, ako vaš poslužitelj prima mnogo DNS paketa, možete povećati veličinu bafera. Ako je bafer pun u trenutku kada stigne novi paket, primjerice jer ga aplikacija sporo obrađuje, počnete gubiti pakete. Naš klijent je ispravno povećao veličinu bafera jer se bojao gubitka podataka, s obzirom da je koristio aplikaciju za prikupljanje metrika putem DNS paketa. Vrijednost koju je postavio bila je maksimalno moguća: 231-1 (ako se postavi 231, jezgra će vratiti 'INVALID ARGUMENT').

Iznenada sam shvatio zašto nmap i scapy rade ispravno: koriste sirove sokete! Sirovi soketi se razlikuju od običnih: rade zaobići iptables i ne buferiraju se!

Ali zašto 'preveliki bafer' uzrokuje probleme? Očigledno ne funkcionira kako je zamišljeno.

U ovom trenutku mogao sam reproducirati problem na nekoliko jezgra i mnoštvu distribucija. Problem se već manifestirao na jezgru 3.x i sada se također manifestirao na jezgru 5.x.

Zaista, kada pokrenem

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

DNS prestaje raditi.

Počeo sam tražiti funkcionalne vrijednosti kroz jednostavni algoritam binarne pretrage i otkrio da sustav radi s 2147481343, no taj broj bio mi je besmislen skup cifara. Predložio sam klijentu da proba tu vrijednost, i on je odgovorio da je sustav proradio s google.com, ali je još uvijek izbacivao grešku s drugim domenama, pa sam nastavio svoje istraživanje.

Instalirao sam dropwatch, alat koji sam trebao ranije iskoristiti: prikazuje gdje točno u jezgru dolazi paket. Krivac se pokazao kao funkcija udp_queue_rcv_skb. Preuzeo sam izvorne datoteke jezgre i dodao nekoliko funkcija printk da pratim gdje konkretno dolazi paket. Brzo sam otkrio potrebno uvjet if, i neko vrijeme sam samo buljio u to, jer je tada sve konačno došlo u cjelinu: 231-1, besmislen broj, neispravna domena… Riječ je o dijelu koda u __udp_enqueue_schedule_skb:

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

Imajte na umu:

  • rmem ima tip int
  • size ima tip u16 (bez potpisanog 16-bitnog int-a) i pohranjuje veličinu paketa
  • sk->sk_rcvbuf ima tip int i pohranjuje veličinu buffera koja je po definiciji jednaka vrijednosti u net.core.rmem_default

Kada sk_rcvbuf približava se 231, zbrajanje veličine paketa može dovesti do cjelobrojnog prelivanja. I budući da je to int, njegova vrijednost postaje negativna, tako da uvjet postaje istinit kada bi trebao biti lažan (više o tome možete saznati iz vezi).

Greška se ispravlja trivijalno: prebacivanjem na unsigned int. Primijenio sam ispravak i ponovno pokrenuo sustav, nakon čega je DNS ponovno proradio.

Okus pobjede

Poslao sam svoje nalaze klijentu i isporučio LKML zakrpu jezgre. Zadovoljan sam: svaki komadić slagalice došao je na svoje mjesto, mogu točno objasniti zašto smo promatrali ono što smo promatrali, i što je najvažnije, uspjeli smo pronaći rješenje problema zahvaljujući zajedničkom radu!

Treba priznati da je slučaj bio rijedak, i srećom, od korisnika rijetko dobivamo toliko složene upite.

Priča o nestalim DNS paketima od podrške Google Clouda


Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster