Lugu kadunud DNS-pakettidest Google Cloudi tugiteenustelt

Toimetaja Google'i blogist: Olete kunagi mõelnud, kuidas Google Cloud Technical Solutions (TSE) insenerid teie tehnilise toe pyyteid käsitlevad? TSE tehnilise toe inseneride ülesanne on tuvastada ja kõrvaldada kasutajate esitatud probleemide allikad. Mõned neist probleemidest on üsna lihtsad, kuid mõnikord on tegemist juhtumiga, mis nõuab mitme inseneri tähelepanu. Sellest artiklist räägib üks TSE töötaja meile ühest väga keerulisest probleemist oma hiljutisest praktikas — DNS-pakettide kadumise juhtum. Selle loo käigus näeme, kuidas insenerid said olukorra lahendatud ja mida nad veast õppisid. Loodame, et see lugu mitte ainult ei tutvusta teile sügavale juurdunud viga, vaid annab ka arusaama protsessidest, mis toimuvad Google Cloud'i toe taotluse esitamisel.

Lugu kadunud DNS-pakettidest Google Cloudi tugiteenustelt

Veadeta lahendamine on samal ajal nii teadus kui kunst. Kõik algab hüpoteesi koostamisest süsteemi ebatavalise käitumise põhjuse kohta, millele järgneb selle vastupidavuse testimine. Siiski, enne kui hüpotees välja formuleeritakse, peame selgelt määratlema ja täpselt sõnastama probleemi. Kui küsimus kõlab liiga ebaselgelt, peate selle hoolikalt läbi analüüsima; selles seisnebki veaotsimise "kunst".

Google Cloudis sellised protsessid keerukuvad mitmekordselt, kuna Google Cloud püüab kõigest väest tagada oma kasutajate privaatsust. Seetõttu ei ole TSE inseneridel juurdepääsu teie süsteemide muutmiseks ega ka võimet vaadata konfigureerimisi nii laialdaselt nagu kasutajad. Seega, et kontrollida mõnda meie hüpoteesi, ei saa me (insenerid) süsteemi kiiresti modifitseerida.

Mõned kasutajad arvavad, et me lahendame kõik nagu mehhaanikud autoteeninduses, lihtsalt saadavad meile virtuaalmasina id, samas kui tegelikult toimub protsess vestluse vormis: teabe kogumine, hüpoteeside koostamine ja kinnitamine (või ümberlükkamine) ning lõpuks problematika lahendamine toimub suhtlemise kaudu kliendiga.

Käesolev probleem

Täna seisame silmitsi hea lõppuga loo ees. Üks põhjus, miks pakutud juhtum edukalt lahendati, on probleemide väga detailne ja täpne kirjeldamine. Allpool on näha esialgse piletiküsimuse koopia (muudetud, et konfidentsiaalset teavet varjata):
Lugu kadunud DNS-pakettidest Google Cloudi tugiteenustelt
Selles teadetes on palju meile kasulikku teavet:

  • On märgitud konkreetne VM
  • On märgitud probleem — DNS ei tööta
  • On näidatud, kus probleem end avaldab — VM ja konteiner
  • On esitatud sammud, mida kasutaja tegi probleemide määramiseks

Pöördumine registreeriti kui «P1: Kriitiline mõju — Teenus pole tootmises kasutatav», mis tähendab pidevat olukorra jälgimist 24/7 «Follow the Sun» schema järgi (link aitab lähemalt lugeda) kasutajapöördumiste prioriteetidest), edastades selle ühelt tehnilise toe meeskonnalt teisele iga ajavööndi vahetusel. Ehk siis hetkeks, mil probleem meie Zürichi meeskonnani jõudis, oli see juba maapinda ümbritsenud. Sel ajal oli kasutaja astunud samme tagajärgede vähendamiseks, kuid kartis, et olukord kordub tootmises, kuna peamine põhjus polnud ikka veel tuvastatud.

Kohale jõudnud piletist oli meil juba järgmine teave:

  • Sisu /etc/hosts
  • Sisu /etc/resolv.conf
  • Kokkuvõte iptables-save
  • Meeskonna kogutud ngrep pcap fail

Nende andmetega olime valmis liikuma «uurimise» ja tõrgete kõrvaldamise etappi.

Meie esimesed sammud

Esiteks kontrollisime logisid ja metaandmete serveri staatust ning veendusime, et see töötab õigesti. Metaandmete server vastab IP-aadressile 169.254.169.254 ja vastutab muu hulgas domeeninimede kontrollimise eest. Kontrollisime ka, et tulemüür töötab VM-i korralikult ja ei blokeeri pakette.

See oli kummaline probleem: nmap'i kontroll kumas meie põhihüpoteesi UDP-pakkide kadumise osas, seega mõtlesime välja veel mitmeid alternatiive ja viise nende kontrollimiseks:

  • Kas paketid kaovad valikuliselt? => Kontrolli iptables reegleid
  • Kas MTU on liiga väike MTU? => Проверить вывод ip a show
  • Kas probleem mõjutab ainult UDP-pakette või ka TCP? => Käivitada dig +tcp
  • Kas genereeritud dig-paketid naasevad? => Käivitada tcpdump
  • Kas libdns töötab õigesti? => Käivitada strace pakettide edastuse kontrollimiseks mõlemast suunast

Siin otsustame helistada kasutajale, et tõrkeotsingut teha reaalajas.

Kõne käigus saame kontrollida mitmeid asju:

  • Pärast mõningaid kontrollimisi välistame iptables reeglid probleemide loendist
  • Kontrollime võrgu liideseid ja marsruutimistabeleid ning kontrollime MTU õigsust
  • Me avastame, et dig +tcp google.com (TCP) töötab nagu peab, aga dig google.com (UDP) ei tööta
  • Käivitades tcpdump praegu töötab dig, avastame, et UDP paketid naasevad
  • Käivitame strace dig google.com ja näeme, kuidas dig õigesti kutsub sendmsg() ja recvms(), kuid teine katkeb timeoute tõttu

Kahjuks tuleb tööpäeva lõpp ja me peame probleemi järgmisse ajavööndisse edastama. Ootamine tekitas siiski meie meeskonnas huvi ning kolleeg pakub välja, et luua algne DNS-pakett Pythoni mooduli scrapy abil.

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

See fragment loob DNS-paketi ja saadab päringu metaandmete serverile.

Kasutaja käivitab koodi, DNS-vastus naaseb ja rakendus saab selle kätte, mis kinnitab, et võrgu tasandil probleeme pole.

Pärast järjekordset "maailmareisi" naaseb päring meie meeskonda ja ma võtan selle täielikult enda peale, arvestades, et kasutajale oleks mugavam, kui päring ei ringi loodaks.

Sel ajal on kasutaja lahkelt nõus jagama süsteemi pilti. Need on suurepärased uudised: võimalus ise süsteemi testida kiirendab oluliselt tõrkeotsingu protsessi, kuna ei pea enam paluma kasutajal käske käivitada, tulemusi saata ja neid analüüsida, saan kõik ise teha!

Kolleegid hakkavad mulle pisut kadestama. Lõuna ajal arutame päringut, kuid kellelgi pole ideid, mis toimub. Õnneks on kasutaja juba astunud samme tagajärgede leevendamiseks ja ei kiirusta kuhugi, nii et meil on aega probleemi analüüsida. Ja kuna meil on pilt, saame teha kõiki meid huvitavaid teste. Suurepärane!

Astudes tagasi sammu

Üks populaarsemaid küsimusi süsteemininseneri ametikohale kandideerimisel on: "Mis juhtub, kui te pingite www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Otsustan rakendada seda HR-i küsimust praegusele probleemile. Üldiselt juhtub, kui üritate määrata DNS-nime, järgnev:

  1. Rakendus kutsub esile süsteemi teeki, näiteks libdns
  2. libdns kontrollib, millise DNS serveri poole süsteem pöörduda (diagrammil on see 169.254.169.254, metadatu server)
  3. libdns kasutab süsteemi kutsungeid UDP sokli (SOCK_DGRAM) loomiseks ja DNS päringute UDP paketide edastamiseks mõlemas suunas
  4. UDP steki seadistamiseks tasemel kernel saab kasutada sysctl liidest
  5. Kernel suhtleb riistvaraga, et edastada pakette võrgus läbi võrguliidese
  6. Hüperviisori ülesanne on tabada ja edastada pakett metadatu serverile, kui see temaga suhtleb
  7. Metadatu server oma võlul määrab DNS nime ja tagastab vastuse sama meetodi abil

Lugu kadunud DNS-pakettidest Google Cloudi tugiteenustelt
Tuletan meelde, milliseid hüpoteese oleme juba arutanud:

Hüpotees: Raamatukogud on rikutud

  • Test 1: käitame süsteemis strace, kontrollime, kas dig kutsub esile õigeid süsteemi kutsungeid
  • Tulemus: õigeid süsteemi kutsungeid kutsutakse esile
  • Test 2: srapy kaudu kontrollida, kas suudame nimesid määrata, mööda minnes süsteemi teekidest
  • Tulemus: suudame
  • Test 3: käitame rpm -V libdns paketi ja teeki md5sum faile
  • Tulemus: teegi kood on täielikult identne töötava operatsioonisüsteemi koodiga
  • Test 4: mountida kasutaja juurenkood VM-le ilma sarnase käitumiseta, teha chroot ja vaadata, kas DNS töötab
  • Tulemus: DNS töötab korrektselt

Testide põhjal järeldus: probleem ei ole raamatukogudes

Hüpotees: DNS seadistustes on viga

  • Test 1: kontrollida tcpdump ja jälgida, kas DNS paketid saadetakse ja naasevad õigesti pärast dig käivitamist
  • Tulemus: paketid edastatakse õigesti
  • Test 2: kontrollida serveris /etc/nsswitch.conf ja /etc/resolv.conf
  • Tulemus: kõik on korras

Testide põhjal järeldus: probleem ei ole DNS seadistustes

Hüpotees: kernel on rikutud

  • Test: installida uus kernel, kontrollida allkirja, taaskäivitada
  • Tulemus: sarnane käitumine

Testide põhjal järeldus: kernel ei ole rikutud

Hüpotees: kasutaja võrgu vale käitumine (kas võrguliidese või hüperviisori liidese)

  • Test 1: kontrollida tulemüüri seadistusi
  • Tulemus: tulemüür laseb DNS paketid nii hostis kui ka GCP-s läbi
  • Test 2: tabada liiklust ja jälgida DNS päringute ja vastuste edastamise ja tagastamise täpsust
  • Tulemus: tcpdump kinnitab, et host saab tagasipööratud pakette kätte

Testide põhjal järeldus: probleem ei ole võrgus

Hüpotees: metadatu server ei tööta

  • Test 1: kontrollida metadatu serveri logisid anomaaliate osas
  • Tulemus: logides anomaaliaid ei esine
  • Test 2: bypass the metadata server via dig @8.8.8.8
  • Result: resolution fails even without the metadata server

Testide põhjal järeldus: the problem is not with the metadata server

Conclusion: we tested all subsystems except the runtime settings!

Diving into the kernel runtime settings

To configure the kernel runtime, you can use the command line options (grub) or the sysctl interface. I checked /etc/sysctl.conf and guess what, I found several custom settings. Feeling like I had grasped something, I dismissed all non-network or non-tcp settings, leaving just a handful of settings net.core. Then I turned to where the VM keeps host permissions and began to apply settings from the broken VM one by one until I found the culprit:

net.core.rmem_default = 2147483647

Here it is, the DNS configuration breaker! I found the weapon of the crime. But why is this happening? I still needed a motive.

The base size of the DNS packet buffer is set via net.core.rmem_default. The typical value ranges somewhere around 200KiB, however, if your server receives many DNS packets, you may want to increase the buffer size. If a new packet arrives while the buffer is full, for instance, because the application is not processing it quickly enough, you will start to lose packets. Our client correctly increased the buffer size as they feared data loss while using a metrics gathering application through DNS packets. The value they set was maximum possible: 231-1 (if set to 231, the kernel returns ‘INVALID ARGUMENT’).

Suddenly I realized why nmap and scapy worked correctly: they used raw sockets! Raw sockets differ from regular ones: they bypass iptables and are not buffered!

But why does the 'too large buffer' cause issues? It clearly doesn’t work as intended.

By this time, I could reproduce the issue on several kernels and many distributions. The problem was evident in kernel 3.x and was now also present in kernel 5.x.

Indeed, when running

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

DNS stopped working.

Alustasin töövõimaluste otsimist lihtsa binaarse otsingu algoritmi kaudu ja avastasin, et süsteem töötab numbril 2147481343, kuid see number oli minu jaoks mõttetu numbrite kogum. Soovitasin kliendil seda numbrit proovida ning tema vastas, et süsteem töötas google.comiga, kuid andis teiste domeenide puhul ikka viga, seega jätkasin oma uurimistööd.

Installisin dropwatch, tööriist, mida oleks pidanud varem kasutama: see näitab, kuhu täpselt tuvastatakse pakett tuumas. Süüdi osutus funktsioon udp_queue_rcv_skb. Laadisin tuuma lähtekoodid alla ja lisasin paar funktsioonide printk et jälgida, kuhu täpselt pakett satub. Leidsin kiiresti vajaliku tingimuse if, ja mõnda aega lihtsalt vaatasin sellele, sest just siis kõik lõpuks klappis: 231-1, mõttetu number, mittetoimiv domeen… See oli küsimus koodijupist __udp_enqueue_schedule_skb:

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

Pange tähele:

  • rmem on tüüp int
  • size on tüüp u16 (mitteallkirjastatud kuusteistbitine int) ja salvestab paketi suuruse
  • sk->sk_rcybuf on tüüp int ja salvestab mälupuhvri suuruse, mis definitsiooni järgi on väärtus net.core.rmem_default

Kui sk_rcvbuf läheneb väärtusele 231, paketi suuruse summeerimine võib viia täisarvuvigani. Kuna see on int, muutub selle väärtus negatiivseks, seega tingimusest saab tõene olukord, kui see peaks olema vale (rohkem uurimist leiate lingil).

Viga parandatakse triviaalselt: muundamisega unsigned int. Rakendasin paranduse ja käivitasin süsteemi uuesti, pärast mida DNS töötas jälle.

Võidu maitse

Saatsin oma leiud kliendile ja edastasin LKML tuuma plaastri. Olen rahul: iga pusle tükk klappis kokku, saan täpselt selgitada, miks me nägime seda, mida nägime, ja mis kõige tähtsam, suutsime leida probleemi lahenduse koostöö kaudu!

Pean tunnistama, et juhtum osutus haruldaseks, ja õnneks ei esita kasutajad meile harva nii keerulisi päringuid.

Lugu kadunud DNS-pakettidest Google Cloudi tugiteenustelt


Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster