История за изчезнали DNS пакети от техническата поддръжка на Google Cloud

От редактора на блога на Google: Задавали ли сте си някога въпроса как инженерите на Google Cloud Technical Solutions (TSE) се справят с вашите искания за техническа поддръжка? Отговорността на инженерите по техническа поддръжка TSE включва откриването и отстраняването на проблеми, посочени от потребителите. Някои от тези проблеми са доста прости, но понякога се появяват искания, изискващи вниманието на няколко инженера. В тази статия един от служителите на TSE ще ни разкаже за един много сложен случай от своята скорошна практика — случаят с изчезващите DNS пакети. В хода на този разказ ще видим как инженерите успяха да разрешат ситуацията и какво ново научиха при отстраняването на грешката. Надяваме се, че тази история не само ще ви разкаже за дълбоко вкоренен бъг, но и ще ви даде разбиране за процесите, които протичат при подаване на искане за поддръжка в Google Cloud.

История за изчезнали DNS пакети от техническата поддръжка на Google Cloud

Отстраняването на проблеми е едновременно наука и изкуство. Всичко започва с изграждането на хипотеза относно причината за нестандартното поведение на системата, след което тя се проверява за устойчивост. Въпреки това, преди да формулираме хипотеза, трябва ясно да определим и точно да формулираме проблема. Ако въпросът е твърде размит, трябва да анализирате всичко подробно; в това се състои "изкуството" на отстраняването на проблеми.

В условията на Google Cloud подобни процеси се усложняват многократно, тъй като Google Cloud полага усилия да гарантира конфиденциалността на своите потребители. Поради това инженерите TSE нямат доступ до редактиране на вашите системи и нямат възможността да преглеждат конфигурациите толкова широко, колкото го правят потребителите. Затова за проверка на някоя от нашите хипотези не можем бързо да модифицираме системата.

Някои потребители смятат, че ще поправим всичко като механиците в автосервиз, и просто ни изпращат ID на виртуалната машина, докато всъщност процесът протича под формата на разговор: събиране на информация, формулиране и потвърждаване (или опровергаване) на хипотези и, в крайна сметка, решаването на проблема се основава на общуването с клиента.

Разглежданият проблем

Днес пред нас е история с добър край. Една от причините за успешното разрешаване на предложената ситуация е много детайлното и точно описание на проблема. По-долу можете да видите копие на първоначалния тикет (редактирано, с цел да се прикрие конфиденциалната информация):
История за изчезнали DNS пакети от техническата поддръжка на Google Cloud
В това съобщение има много полезна информация за нас:

  • Посочена е конкретна VM
  • Ясно е какъв е проблемът - не работи DNS
  • Посочено е къде се проявява проблемът - VM и контейнер
  • Посочени са стъпките, които потребителят е предприел за определяне на проблема

Заявеното е регистрирано като «P1: Критично въздействие — Услугата е неизползваема в продукция», което означава постоянен контрол на ситуацията 24/7 по схемата «Следвай слънцето» (по линка можете да прочетете повече за приоритетите на потребителските обращения), с прехвърляне от един екип за поддръжка на друг при всяко изменение на часовите зони. Всъщност, когато проблемът стигна до нашия екип в Цюрих, той беше обиколил целия свят. Към този момент потребителят беше предприел мерки за намаляване на последствията, но се притесняваше от повторение на ситуацията в продукция, тъй като основната причина все още не беше открита.

Когато тикетът стигна до Цюрих, вече имаше следната информация:

  • Съдържание /etc/hosts
  • Съдържание /etc/resolv.conf
  • Извод iptables-save
  • Събран от екипа ngrep pcap файл

С тези данни бяхме готови да започнем етапа на «разследване» и отстраняване на неизправности.

Нашите първи стъпки

На първо място проверихме логовете и статуса на сървъра за метаданни и се уверихме, че работи коректно. Сървърът за метаданни отговаря на IP адрес 169.254.169.254 и, освен всичко друго, отговаря за управлението на имената на домейни. Ние също така проверихме, че защитната стена работи коректно с VM и не блокира пакетите.

Това беше някакъв странен проблем: проверката с nmap опроверга основната ни хипотеза за загуба на UDP пакети, затова помислено извеждаме още няколко варианта и методи за проверка:

  • Пропадат ли пакетите по избор? => Проверете правилата на iptables
  • Не е ли прекалено малък MTU? => Проверить вывод ip a show
  • Засегната ли е проблема само UDP пакети или и TCP? => Прокарайте dig +tcp
  • Връщат ли се генерираните пакети от dig? => Прокарайте tcpdump
  • Работи ли коректно libdns? => Прокарайте strace за проверка на предаването на пакетите в двете посоки

Тук решаваме да се свържем с потребителя за отстраняване на неизправности на живо.

По време на разговора успяваме да проверим няколко неща:

  • След няколко проверки изключваме правилата на iptables от списъка с причини
  • Проверяваме мрежовите интерфейси и таблиците за маршрутизиране и преглеждаме отново коректността на MTU
  • Откриваме, че dig +tcp google.com работи правилно, но dig google.com (UDP) не работи
  • Провеждайки tcpdump все още работи dig, откриваме, че UDP пакетите се връщат
  • Провеждаме strace dig google.com и виждаме как dig правилно извиква sendmsg() и recvms(), но второто прекъсва поради таймаут

За съжаление, сменя се и ние сме принудени да предадем проблема на следващата времева зона. Все пак, въпросът предизвика интерес в нашия екип, и колега предлага да създадем изходен DNS пакет с питоновия модул 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())

Този фрагмент създава DNS пакет и изпраща заявка до сървъра за метаданни.

Потребителят стартира кода, DNS отговорът се връща и приложението го получава, което потвърдява, че проблемът не е на ниво мрежа.

След поредното "около света" запитването се връща при нашия екип, и аз напълно поемам отговорността за него, като считам, че на потребителя ще му бъде удобно, ако запитването спре да обикаля от място на място.

Междувременно, потребителят любезно се съгласява да предостави образа на системата. Това са много добри новини: възможността да тествам системата сам значително ускорява отстраняването на проблеми, тъй като вече не ми е необходимо да моля потребителя да стартира команди, да ми изпраща резултати и да ги анализирам, всичко мога да направя сам!

Колегите започнат да ми завиждат малко. На обяда обсъждаме запитването, но никой няма идеи за това какво се случва. За щастие, самият потребител вече е взел мерки за облекчаване на последствията и няма да бърза, така че имаме време да разследваме проблема. И тъй като имаме образ, можем да извършваме всякакви тестове, които ни интересуват. Отлично!

Връщайки се на една крачка назад

Един от най-популярните въпроси на интервю за позицията системен инженер е: "Какво се случва, когато пингувате www.google.com?» Вопрос шикарный, так как кандидату необходимо описать пусть от оболочки до пользовательского пространства, до ядра системы и далее к сети. Я улыбаюсь: иногда вопросы с интервью оказываются полезны и в реальной жизни…

Решавам да приложа този HR въпрос към текущия проблем. Грубо казано, когато се опитвате да определите DNS името, се случва следното:

  1. Приложението извиква системната библиотека, например libdns
  2. libdns проверява конфигурацията на системата за кой DNS сървър да се свърже (на схемата това е 169.254.169.254, сървър за метаданни)
  3. libdns използва системни повиквания за създаване на UDP сокет (SOKET_DGRAM) и предаване на UDP пакети с DNS запитвания в двете посоки
  4. Чрез интерфейса sysctl може да се конфигурира UDP стека на ниво ядрo
  5. Ядрото взаимодейства с хардуера за предаване на пакети по мрежата през мрежовия интерфейс
  6. Хипервизорът улавя и предава пакета на сървъра за метаданни при контакт с него
  7. Сървърът за метаданни със своето магьосничество определя DNS името и по същия начин връща отговора

История за изчезнали DNS пакети от техническата поддръжка на Google Cloud
Напомням ви, какви хипотези вече разгледахме:

Хипотеза: Счупени библиотеки

  • Тест 1: да се пусне strace в системата, да се провери дали dig извиква коректни системни повиквания
  • Резултат: извикват се коректни системни повиквания
  • Тест 2: да проверим дали можем да определяме имена, заобикаляйки системните библиотеки
  • Резултат: можем
  • Тест 3: да се пусне rpm –V на пакета libdns и md5sum файловете на библиотеката
  • Резултат: кодът на библиотеката е напълно идентичен на кода в работната операционна система
  • Тест 4: да монтираме образа на кореновата система на потребителя на VM без подобно поведение, да се пусне chroot, да се провери работи ли DNS
  • Резултат: DNS работи коректно

Извод на базата на тестовете: проблемът не е в библиотеките

Хипотеза: Има грешка в конфигурацията на DNS

  • Тест 1: да проверим tcpdump и да наблюдаваме дали DNS пакетите се изпращат и връщат коректно след стартиране на dig
  • Резултат: пакетите се предават коректно
  • Тест 2: да се проверят настройките на сървъра /etc/nsswitch.conf и /etc/resolv.conf
  • Резултат: всичко е коректно

Извод на базата на тестовете: проблемът не е в конфигурацията на DNS

Хипотеза: ядрото е повредено

  • Тест: да се инсталира ново ядро, да се провери подписа, да се рестартира
  • Резултат: аналогично поведение

Извод на базата на тестовете: ядрото не е повредено

Хипотеза: некоректно поведение на потребителската мрежа (или на интерфейса на мрежата на хипервизора)

  • Тест 1: да се проверят настройките на защитната стена
  • Резултат: защитната стена пропуска DNS пакети и на хоста, и на GCP
  • Тест 2: да се прихване трафика и да се проследи коректността на предаването и връщането на DNS запитвания
  • Резултат: tcpdump потвърджава получаването на обратни пакети от хоста

Извод на базата на тестовете: проблемът не е в мрежата

Хипотеза: не работи сървъра за метаданни

  • Тест 1: да се проверят логовете на сървъра за метаданни за аномалии
  • Резултат: в логовете няма аномалии
  • Тест 2: заобикаляне на сървъра за метаданни чрез dig @8.8.8.8
  • Резултат: разрешенията се нарушават дори без използване на сървъра за метаданни

Извод на базата на тестовете: проблемът не е в сървъра за метаданни

Резюме: тествахме всички подсистеми освен настройките на средата за изпълнение!

Потапяйки се в настройките на средата за изпълнение на ядрото

За настройване на средата за изпълнение на ядрото можете да използвате опциите на командния ред (grub) или интерфейса sysctl. Надникнах в /etc/sysctl.conf и си помислих, че открих няколко персонализирани настройки. Усещайки, че съм се хванал за нещо, отхвърлих всички не-мрежови или не-tcp настройки, оставайки с куп настройки net.core. След това се обърнах към мястото, където VM съхранява разрешенията на хоста и започнах да прилагам една след друга настройките от счупената VM, докато не попаднах на виновника:

net.core.rmem_default = 2147483647

Ето я, нарушаваща DNS конфигурация! Намерих оръдието на престъплението. Но защо се случва това? Все още ми трябваше мотив.

Настройката на основния размер на буфера за DNS пакети се извършва чрез net.core.rmem_default. Типичната стойност варира около 200КиБ, но ако вашият сървър получава много DNS пакети, можете да увеличите размера на буфера. Ако в момента на постъпването на нов пакет буферът е пълен, например защото приложението не го обработва достатъчно бързо, ще започнете да губите пакети. Нашият клиент правилно увеличи размера на буфера, тъй като се страхуваше от загуба на данни, докато използваше приложение за събиране на метрики чрез DNS пакети. Стойността, която зададе, беше максимално възможната: 231-1 (ако зададете 231, ядрото ще върне „INVALID ARGUMENT“).

Изведнъж прозрях защо nmap и scapy работеха коректно: те използваха сурови сокети! Суровите сокети се различават от обичайните: те работят заобиколно на iptables и не се буферират!

Но защо „прекалено голям буфер“ създава проблеми? Явно не работи по предвидения начин.

Към този момент можех да възпроизведа проблема на няколко ядра и множество дистрибуции. Проблемът вече се проявяваше на ядро 3.x и сега също на ядро 5.x.

Наистина, при стартиране на

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

DNS спираше да работи.

Започнах да търся работни стойности чрез прост алгоритъм за бинарно търсене и открих, че с 2147481343 системата работи, но това число беше за мен безсмислен набор от цифри. Предложих на клиента да пробва това число и той ми отговори, че системата заработи с google.com, но все още показваше грешка с другите домейни, затова продължих моето разследване.

Инсталирах dropwatch, инструмент, който трябваше да използвам по-рано: той показва къде точно в ядрото попада пакет. Виновна се оказа функцията udp_queue_rcv_skb. Свалих източниците на ядрото и добавих няколко функции printk за да проследя къде точно попада пакетът. Бързо открих нужното условие if, и известно време просто зяпах в него, защото точно тогава всичко най-накрая се свърза в единна картина: 231-1, безсмислено число, неработещ домейн… Работата беше в част от кода в __udp_enqueue_schedule_skb:

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

Обърнете внимание:

  • rmem е от тип int
  • size е от тип u16 (несигурен 16-битов int) и съхранява размера на пакета
  • sk->sk_rcybuf е от тип int и съхранява размера на буфера, който по определение е равен на стойността в net.core.rmem_default

Когато sk_rcvbuf приближаваща се до 231, сумата на размера на пакета може да доведе до целочислено преливане. И тъй като е int, стойността му става отрицателна, така че условието става истинско, когато трябва да бъде фалшиво (повече за това може да прочетете в връзка).

Грешката се коригира по прост начин: преобразуване в unsigned int. Приложих поправката и рестартах системата, след което DNS отново заработи.

Вкусът на победата

Изпратих находките си на клиента и изпратих LKML корекцията на ядрото. Доволен съм: всеки парче от пъзела се събра в едно цяло, мога точно да обясня защо наблюдавахме това, което наблюдавахме, и най-важното, успяхме да намерим решение на проблема благодарение на съвместната работа!

Трябва да призная, че случаят се оказа рядък, и за щастие рядко получаваме толкова сложни запитвания от потребителите.

История за изчезнали DNS пакети от техническата поддръжка на Google Cloud


Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster