Tarina puuttuvista DNS-paketteista Google Cloudin tekniseltä tuelta

Google-blogieditorista: Oletko koskaan miettinyt, kuinka Google Cloud Technical Solutions (TSE) -insinöörit käsittelevät tukipyyntösi? TSE:n teknisen tuen insinöörit ovat vastuussa käyttäjien ilmoittamien ongelmalähteiden tunnistamisesta ja korjaamisesta. Jotkut näistä ongelmista ovat melko yksinkertaisia, mutta joskus törmäät lippuun, joka vaatii useiden insinöörien huomion kerralla. Tässä artikkelissa yksi TSE:n työntekijöistä kertoo meille yhdestä erittäin hankalasta ongelmasta viimeaikaisesta käytännöstään - puuttuvien DNS-pakettien tapauksessa. Tässä tarinassa näemme, kuinka insinöörit onnistuivat ratkaisemaan tilanteen ja mitä uutta he oppivat korjaaessaan virhettä. Toivomme, että tämä tarina ei vain opasta sinua syvälle juurtuneesta virheestä, vaan antaa myös käsityksen prosesseista, jotka liittyvät tukilipun jättämiseen Google Cloudiin.

Tarina puuttuvista DNS-paketteista Google Cloudin tekniseltä tuelta

Vianetsintä on sekä tiedettä että taidetta. Kaikki alkaa luomalla hypoteesi järjestelmän epätyypillisen käyttäytymisen syystä, minkä jälkeen sen vahvuus testataan. Ennen kuin muotoilemme hypoteesin, meidän on kuitenkin määriteltävä selkeästi ja muotoiltava ongelma tarkasti. Jos kysymys kuulostaa liian epämääräiseltä, sinun on analysoitava kaikki huolellisesti; Tämä on vianetsinnän "taidetta".

Google Cloudissa tällaiset prosessit muuttuvat eksponentiaalisesti monimutkaisemmiksi, kun Google Cloud yrittää parhaansa mukaan taata käyttäjiensä yksityisyyden. Tämän vuoksi TSE-suunnittelijoilla ei ole oikeutta muokata järjestelmiäsi, eikä myöskään mahdollisuutta tarkastella määrityksiä yhtä laajasti kuin käyttäjillä. Siksi emme (insinöörit) voi muuttaa järjestelmää nopeasti testataksemme mitään hypoteeseistamme.

Jotkut käyttäjät uskovat, että korjaamme kaiken kuten mekaniikka autohuollossa ja lähetämme meille vain virtuaalikoneen tunnuksen, kun taas todellisuudessa prosessi tapahtuu keskustelumuodossa: keräämme tietoa, muodostamme ja vahvistamme (tai kumoamme) hypoteeseja, ja lopulta päätöksentekoongelmat perustuvat kommunikointiin asiakkaan kanssa.

Kyseinen ongelma

Tänään meillä on tarina, jolla on hyvä loppu. Yksi syy ehdotetun tapauksen onnistuneeseen ratkaisuun on erittäin yksityiskohtainen ja tarkka kuvaus ongelmasta. Alla näet kopion ensimmäisestä lipusta (muokattu piilottamaan luottamukselliset tiedot):
Tarina puuttuvista DNS-paketteista Google Cloudin tekniseltä tuelta
Tämä viesti sisältää meille paljon hyödyllistä tietoa:

  • Tietty VM määritetty
  • Itse ongelma ilmoitetaan - DNS ei toimi
  • Osoitetaan, missä ongelma ilmenee - VM ja kontti
  • Käyttäjän toimet ongelman tunnistamiseksi on osoitettu.

Pyyntö rekisteröitiin nimellä "P1: Kriittinen vaikutus - Palvelu käyttökelvoton tuotannossa", mikä tarkoittaa jatkuvaa tilanteen seurantaa 24/7 "Seuraa aurinkoa" -järjestelmän mukaisesti (voit lukea lisää käyttäjien pyyntöjen prioriteetit), jolloin se siirtyy yhdeltä tekniseltä tukitiimiltä toiselle jokaisen aikavyöhykevaihdon yhteydessä. Itse asiassa siihen mennessä, kun ongelma saavutti tiimimme Zürichissä, se oli jo kiertänyt maapallon. Tähän mennessä käyttäjä oli ryhtynyt lieventäviin toimiin, mutta pelkäsi tilanteen toistumista tuotannossa, koska perimmäistä syytä ei ollut vielä löydetty.

Kun lippu saapui Zürichiin, meillä oli jo seuraavat tiedot käsillä:

  • Sisältö /etc/hosts
  • Sisältö /etc/resolv.conf
  • johtopäätös iptables-save
  • Joukkueen kokoama ngrep pcap tiedosto

Näillä tiedoilla olimme valmiit aloittamaan "tutkinta" ja vianetsintävaiheen.

Ensimmäiset askeleemme

Ensinnäkin tarkistimme metatietopalvelimen lokit ja tilan ja varmistimme, että se toimii oikein. Metatietopalvelin vastaa IP-osoitteeseen 169.254.169.254 ja on muun muassa vastuussa verkkotunnusten hallinnasta. Tarkistimme myös, että palomuuri toimii oikein VM:n kanssa eikä estä paketteja.

Se oli jonkinlainen outo ongelma: nmap-tarkistus kumosi päähypoteesimme UDP-pakettien katoamisesta, joten keksimme mielessämme useita muita vaihtoehtoja ja tapoja tarkistaa ne:

  • Pudotetaanko paketteja valikoivasti? => Tarkista iptablesin säännöt
  • Eikö se ole liian pieni? MTU? => Tarkista lähtö ip a show
  • Koskeeko ongelma myös vain UDP-paketteja tai TCP:tä? => Aja pois dig +tcp
  • Palautetaanko dig generoidut paketit? => Aja pois tcpdump
  • Toimiiko libdns oikein? => Aja pois strace tarkistaaksesi pakettien lähetyksen molempiin suuntiin

Täällä päätämme soittaa käyttäjälle ongelmien vianmääritykseen livenä.

Puhelun aikana voimme tarkistaa useita asioita:

  • Useiden tarkistusten jälkeen suljemme pois iptables-säännöt syyluettelosta
  • Tarkistamme verkkoliitännät ja reititystaulukot ja tarkistamme, että MTU on oikea
  • Me huomaamme sen dig +tcp google.com (TCP) toimii kuten pitääkin, mutta dig google.com (UDP) ei toimi
  • Ajettuaan pois tcpdump työskennellessä dig, huomaamme, että UDP-paketteja palautetaan
  • Ajamme pois strace dig google.com ja näemme kuinka kaivaa oikein kutsuu sendmsg() и recvms(), mutta toinen katkaisee aikakatkaisun

Valitettavasti työvuoron loppu tulee ja joudumme eskaloimaan ongelman seuraavalle aikavyöhykkeelle. Pyyntö kuitenkin heräsi tiimissämme kiinnostusta, ja eräs kollega ehdottaa alkuperäisen DNS-paketin luomista raa'alla Python-moduulilla.

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

Tämä fragmentti luo DNS-paketin ja lähettää pyynnön metatietopalvelimelle.

Käyttäjä suorittaa koodin, DNS-vastaus palautetaan ja sovellus vastaanottaa sen vahvistaen, että verkkotasolla ei ole ongelmaa.

Toisen "ympärimatkan" jälkeen pyyntö palaa tiimillemme ja siirrän sen kokonaan itselleni, ajatellen, että käyttäjälle on mukavampaa, jos pyyntö lakkaa kiertämästä paikasta toiseen.

Sillä välin käyttäjä suostuu ystävällisesti toimittamaan tilannekuvan järjestelmäkuvasta. Tämä on erittäin hyvä uutinen: mahdollisuus testata järjestelmää itse nopeuttaa vianmääritystä paljon, koska minun ei enää tarvitse pyytää käyttäjää suorittamaan komentoja, lähettää minulle tuloksia ja analysoida niitä, voin tehdä kaiken itse!

Kollegani ovat alkaneet hieman kadehtia minua. Lounaalla keskustelemme kääntymyksestä, mutta kenelläkään ei ole aavistustakaan siitä, mitä tapahtuu. Onneksi käyttäjä itse on jo ryhtynyt toimiin seurausten lieventämiseksi, eikä hänellä ole kiirettä, joten meillä on aikaa käsitellä ongelmaa. Ja koska meillä on imago, voimme suorittaa mitä tahansa meitä kiinnostavia testejä. Loistava!

Ottaa askeleen taaksepäin

Yksi suosituimmista järjestelmäinsinöörien haastattelukysymyksistä on: ”Mitä tapahtuu, kun pingat www.google.com? Kysymys on hieno, koska ehdokkaan on kuvattava kaikki kuoresta käyttäjätilaan, järjestelmäytimeen ja sitten verkkoon. Hymyilen: joskus haastattelukysymykset osoittautuvat hyödyllisiksi tosielämässä...

Päätän soveltaa tätä HR-kysymystä nykyiseen ongelmaan. Karkeasti sanottuna, kun yrität määrittää DNS-nimen, tapahtuu seuraavaa:

  1. Sovellus kutsuu järjestelmäkirjastoa, kuten libdns
  2. libdns tarkistaa järjestelmän asetukset mihin DNS-palvelimeen sen tulee ottaa yhteyttä (kaaviossa tämä on 169.254.169.254, metatietopalvelin)
  3. libdns käyttää järjestelmäkutsuja UDP-socketin (SOKET_DGRAM) luomiseen ja UDP-pakettien lähettämiseen DNS-kyselyllä molempiin suuntiin
  4. Sysctl-rajapinnan kautta voit määrittää UDP-pinon ytimen tasolla
  5. Ydin on vuorovaikutuksessa laitteiston kanssa ja lähettää paketteja verkon yli verkkoliitännän kautta
  6. Hypervisor sieppaa ja lähettää paketin metatietopalvelimelle koskettaessaan sitä
  7. Metatietopalvelin määrittää taianomaisesti DNS-nimen ja palauttaa vastauksen samalla menetelmällä

Tarina puuttuvista DNS-paketteista Google Cloudin tekniseltä tuelta
Haluan muistuttaa, mitä hypoteeseja olemme jo harkinneet:

Hypoteesi: rikkinäiset kirjastot

  • Testi 1: suorita strace järjestelmässä, tarkista, että dig kutsuu oikeat järjestelmäkutsut
  • Tulos: Soitetaan oikeat järjestelmäkutsut
  • Testi 2: srapyn avulla tarkistamme, voimmeko määrittää nimet ohittamalla järjestelmäkirjastot
  • Tulos: voimme
  • Testi 3: suorita rpm –V libdns-paketissa ja md5sum-kirjastotiedostoissa
  • Tulos: kirjastokoodi on täysin identtinen toimivan käyttöjärjestelmän koodin kanssa
  • Testi 4: liitä käyttäjän juurijärjestelmän näköistiedosto virtuaalikoneeseen ilman tätä toimintaa, suorita chroot ja katso toimiiko DNS
  • Tulos: DNS toimii oikein

Johtopäätös testien perusteella: ongelma ei ole kirjastoissa

Hypoteesi: DNS-asetuksissa on virhe

  • Testi 1: tarkista tcpdump ja katso, lähetetäänkö ja palautetaanko DNS-paketit oikein dig-komennon suorittamisen jälkeen
  • Tulos: paketit lähetetään oikein
  • Testi 2: Tarkista palvelin uudelleen /etc/nsswitch.conf и /etc/resolv.conf
  • Tulos: kaikki on oikein

Johtopäätös testien perusteella: ongelma ei ole DNS-kokoonpanossa

Hypoteesi: ydin vaurioitunut

  • Testi: asenna uusi ydin, tarkista allekirjoitus, käynnistä uudelleen
  • Tulos: samanlainen käyttäytyminen

Johtopäätös testien perusteella: ydin ei ole vaurioitunut

Hypoteesi: käyttäjäverkon (tai hypervisor-verkkoliittymän) virheellinen toiminta

  • Testi 1: Tarkista palomuurin asetukset
  • Tulos: palomuuri välittää DNS-paketit sekä isännässä että GCP:ssä
  • Testi 2: siepata liikennettä ja tarkkaile DNS-pyyntöjen lähetyksen ja palautuksen oikeellisuutta
  • Tulos: tcpdump vahvistaa, että isäntä on vastaanottanut palautuspaketit

Johtopäätös testien perusteella: ongelma ei ole verkossa

Hypoteesi: metatietopalvelin ei toimi

  • Testi 1: tarkista metatietopalvelimen lokit poikkeavuuksien varalta
  • Tulos: lokeissa ei ole poikkeamia
  • Testi 2: Ohita metatietopalvelin kautta dig @8.8.8.8
  • Tulos: Resoluutio on rikki jopa ilman metatietopalvelinta

Johtopäätös testien perusteella: ongelma ei ole metatietopalvelimessa

Bottom line: testasimme kaikkia alijärjestelmiä paitsi ajonaikaiset asetukset!

Sukella ytimen suoritusajan asetuksiin

Ytimen suoritusympäristön määrittämiseen voit käyttää komentorivin valintoja (grub) tai sysctl-liitäntää. Katsoin sisään /etc/sysctl.conf ja ajattele vain, löysin useita mukautettuja asetuksia. Tuntui kuin olisin tarttunut johonkin, hylkäsin kaikki ei-verkko- tai ei-tcp-asetukset ja jäin vuoristoasetuksiin. net.core. Sitten menin sinne, missä isäntäoikeudet olivat VM:ssä ja aloin soveltaa asetuksia yksitellen, yksi kerrallaan, rikkinäisen virtuaalikoneen kanssa, kunnes löysin syyllisen:

net.core.rmem_default = 2147483647

Tässä se on, DNS-rikkoava kokoonpano! Löysin murhaaseen. Mutta miksi näin tapahtuu? Tarvitsin silti motiivin.

DNS-pakettipuskurin peruskoko määritetään käyttämällä net.core.rmem_default. Tyypillinen arvo on jossain 200 KiB, mutta jos palvelimesi vastaanottaa paljon DNS-paketteja, kannattaa ehkä lisätä puskurin kokoa. Jos puskuri on täynnä uuden paketin saapuessa, esimerkiksi koska sovellus ei käsittele sitä tarpeeksi nopeasti, paketteja alkaa hävitä. Asiakkaamme lisäsi puskurin kokoa oikein, koska hän pelkäsi tietojen katoamista, koska hän käytti sovellusta, joka kerää mittareita DNS-pakettien kautta. Hänen asettama arvo oli suurin mahdollinen: 231-1 (jos arvoksi on asetettu 231, ydin palauttaa "INVALID ARGUMENT").

Yhtäkkiä tajusin, miksi nmap ja scapy toimivat oikein: he käyttivät raakapistokkeita! Raw socketit eroavat tavallisista socketeista: ne ohittavat iptables, eikä niitä puskuroida!

Mutta miksi "liian suuri puskuri" aiheuttaa ongelmia? Se ei selvästikään toimi tarkoitetulla tavalla.

Tässä vaiheessa voisin toistaa ongelman useissa ytimissä ja useissa jakeluissa. Ongelma ilmeni jo 3.x-ytimessä ja nyt se ilmeni myös 5.x-ytimessä.

Itse asiassa käynnistyksen yhteydessä

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

DNS lakkasi toimimasta.

Aloin etsiä työarvoja yksinkertaisen binäärihakualgoritmin avulla ja huomasin, että järjestelmä toimi 2147481343:n kanssa, mutta tämä numero oli minulle merkityksetön numerosarja. Ehdotin asiakasta kokeilemaan tätä numeroa, ja hän vastasi, että järjestelmä toimi google.comin kanssa, mutta antoi silti virheen muissa verkkotunnuksissa, joten jatkoin tutkimustani.

Olen asentanut pudotuskello, työkalu, jota olisi pitänyt käyttää aiemmin: se näyttää tarkalleen, mihin ytimeen paketti päätyy. Syyllinen oli toiminto udp_queue_rcv_skb. Latasin ytimen lähteet ja lisäsin muutaman toiminto printk seurataksesi, mihin paketti tarkalleen päätyy. Löysin nopeasti oikean tilan if, ja vain tuijotti sitä jonkin aikaa, koska silloin kaikki lopulta koottiin kokonaiseksi kuvaksi: 231-1, merkityksetön numero, toimimaton verkkotunnus... Se oli koodinpätkä __udp_enqueue_schedule_skb:

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

Huomaa:

  • rmem on tyyppiä int
  • size on tyyppiä u16 (signed XNUMX-bittinen int) ja tallentaa paketin koon
  • sk->sk_rcybuf on tyyppiä int ja tallentaa puskurin koon, joka määritelmän mukaan on yhtä suuri kuin arvo in net.core.rmem_default

Kun sk_rcvbuf lähestyy arvoa 231, paketin koon summaus voi johtaa kokonaislukujen ylivuoto. Ja koska se on int, sen arvosta tulee negatiivinen, joten ehdosta tulee tosi, kun sen pitäisi olla epätosi (voit lukea lisää tästä osoitteessa linkki).

Virhe voidaan korjata triviaalilla tavalla: heittämällä unsigned int. Otin korjauksen käyttöön ja käynnistin järjestelmän uudelleen ja DNS toimi taas.

Voiton maku

Välitin havaintoni asiakkaalle ja lähetin LKML ytimen korjaustiedosto. Olen tyytyväinen: jokainen palapelin pala sopii yhteen, osaan selittää tarkalleen, miksi havaitsimme sen, mitä havaitsimme, ja mikä tärkeintä, onnistuimme löytämään ratkaisun ongelmaan tiimityömme ansiosta!

On syytä huomata, että tapaus osoittautui harvinaiseksi, ja onneksi saamme harvoin niin monimutkaisia ​​​​pyyntöjä käyttäjiltä.

Tarina puuttuvista DNS-paketteista Google Cloudin tekniseltä tuelta


Lähde: will.com

Lisää kommentti