Kisah tentang paket DNS yang hilang dari dukungan teknis Google Cloud

Dari Editor Blog Google: Pernahkah Anda bertanya-tanya bagaimana teknisi Solusi Teknis Google Cloud (TSE) menangani permintaan dukungan Anda? Insinyur Dukungan Teknis TSE bertanggung jawab untuk mengidentifikasi dan memperbaiki sumber masalah yang dilaporkan pengguna. Beberapa masalah ini cukup sederhana, namun terkadang Anda menemukan tiket yang memerlukan perhatian beberapa teknisi sekaligus. Dalam artikel ini, salah satu karyawan TSE akan memberi tahu kita tentang satu masalah yang sangat rumit dari praktiknya baru-baru ini - kasus paket DNS hilang. Dalam cerita ini, kita akan melihat bagaimana para insinyur berhasil menyelesaikan situasi tersebut, dan hal-hal baru apa yang mereka pelajari saat memperbaiki kesalahan tersebut. Kami berharap cerita ini tidak hanya mendidik Anda tentang bug yang mengakar, namun juga memberi Anda wawasan tentang proses yang diperlukan untuk mengajukan tiket dukungan ke Google Cloud.

Kisah tentang paket DNS yang hilang dari dukungan teknis Google Cloud

Pemecahan masalah adalah ilmu dan seni. Semuanya dimulai dengan membangun hipotesis tentang alasan perilaku sistem yang tidak standar, setelah itu diuji kekuatannya. Namun, sebelum kita merumuskan hipotesis, kita harus mendefinisikan dengan jelas dan merumuskan masalahnya secara tepat. Jika pertanyaannya terdengar terlalu kabur, Anda harus menganalisis semuanya dengan cermat; Ini adalah “seni” pemecahan masalah.

Di bawah Google Cloud, proses tersebut menjadi jauh lebih kompleks karena Google Cloud berupaya semaksimal mungkin untuk menjamin privasi penggunanya. Oleh karena itu, teknisi TSE tidak memiliki akses untuk mengedit sistem Anda, atau kemampuan untuk melihat konfigurasi seluas yang dimiliki pengguna. Oleh karena itu, untuk menguji hipotesis kami, kami (insinyur) tidak dapat dengan cepat memodifikasi sistem.

Beberapa pengguna percaya bahwa kami akan memperbaiki semuanya seperti mekanik di layanan mobil, dan cukup mengirimkan kami id mesin virtual, sedangkan pada kenyataannya proses tersebut berlangsung dalam format percakapan: mengumpulkan informasi, membentuk dan mengkonfirmasi (atau menyangkal) hipotesis, dan, pada akhirnya, pengambilan keputusan masalah didasarkan pada komunikasi dengan klien.

Masalah yang dimaksud

Hari ini kita memiliki cerita dengan akhir yang bagus. Salah satu alasan keberhasilan penyelesaian kasus yang diajukan adalah uraian masalahnya yang sangat rinci dan akurat. Di bawah ini Anda dapat melihat salinan tiket pertama (diedit untuk menyembunyikan informasi rahasia):
Kisah tentang paket DNS yang hilang dari dukungan teknis Google Cloud
Pesan ini berisi banyak informasi berguna bagi kami:

  • VM tertentu ditentukan
  • Masalahnya sendiri ditunjukkan - DNS tidak berfungsi
  • Ini ditunjukkan di mana masalah muncul - VM dan container
  • Langkah-langkah yang diambil pengguna untuk mengidentifikasi masalah ditunjukkan.

Permintaan tersebut terdaftar sebagai “P1: Dampak Kritis - Layanan Tidak Dapat Digunakan dalam Produksi”, yang berarti pemantauan situasi secara terus-menerus 24/7 sesuai dengan skema “Ikuti Matahari” (Anda dapat membaca lebih lanjut tentang prioritas permintaan pengguna), dengan perpindahannya dari satu tim dukungan teknis ke tim lainnya dengan setiap pergeseran zona waktu. Faktanya, saat masalah ini sampai ke tim kami di Zurich, masalah tersebut sudah menyebar ke seluruh dunia. Pada saat ini, pengguna telah mengambil langkah-langkah mitigasi, namun takut akan terulangnya situasi di produksi, karena akar permasalahannya belum ditemukan.

Pada saat tiket sampai di Zurich, kami sudah memiliki informasi berikut:

  • Konten /etc/hosts
  • Konten /etc/resolv.conf
  • Keluaran iptables-save
  • Dikumpulkan oleh tim ngrep file pcap

Dengan data ini, kami siap memulai tahap “investigasi” dan pemecahan masalah.

Langkah pertama kami

Pertama-tama, kami memeriksa log dan status server metadata dan memastikannya berfungsi dengan benar. Server metadata merespons alamat IP 169.254.169.254 dan, antara lain, bertanggung jawab untuk mengendalikan nama domain. Kami juga memeriksa ulang apakah firewall berfungsi dengan benar dengan VM dan tidak memblokir paket.

Itu adalah masalah yang aneh: pemeriksaan nmap menyangkal hipotesis utama kami tentang hilangnya paket UDP, jadi kami secara mental memikirkan beberapa opsi dan cara lagi untuk memeriksanya:

  • Apakah paket dijatuhkan secara selektif? => Periksa aturan iptables
  • Bukankah itu terlalu kecil? MTU? => Periksa keluaran ip a show
  • Apakah masalahnya hanya mempengaruhi paket UDP atau TCP juga? => Berkendara pergi dig +tcp
  • Apakah paket yang dihasilkan dig dikembalikan? => Berkendara pergi tcpdump
  • Apakah libdns berfungsi dengan benar? => Berkendara pergi strace untuk memeriksa transmisi paket di kedua arah

Di sini kami memutuskan untuk memanggil pengguna untuk memecahkan masalah secara langsung.

Selama panggilan kami dapat memeriksa beberapa hal:

  • Setelah beberapa kali pemeriksaan, kami mengecualikan aturan iptables dari daftar alasan
  • Kami memeriksa antarmuka jaringan dan tabel perutean, dan memeriksa ulang apakah MTU sudah benar
  • Kami menemukan itu dig +tcp google.com (TCP) berfungsi sebagaimana mestinya, tapi dig google.com (UDP) tidak berfungsi
  • Setelah diusir tcpdump saat bekerja dig, kami menemukan bahwa paket UDP dikembalikan
  • Kami pergi strace dig google.com dan kita melihat bagaimana dig memanggil dengan benar sendmsg() и recvms(), namun yang kedua diinterupsi oleh batas waktu

Sayangnya, akhir peralihan telah tiba dan kami terpaksa meneruskan masalah ke zona waktu berikutnya. Namun, permintaan tersebut membangkitkan minat tim kami, dan seorang rekan menyarankan untuk membuat paket DNS awal menggunakan modul Python yang kasar.

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

Fragmen ini membuat paket DNS dan mengirimkan permintaan ke server metadata.

Pengguna menjalankan kode, respons DNS dikembalikan, dan aplikasi menerimanya, mengonfirmasi bahwa tidak ada masalah di tingkat jaringan.

Setelah “perjalanan keliling dunia” lainnya, permintaan tersebut kembali ke tim kami, dan saya sepenuhnya mentransfernya ke diri saya sendiri, berpikir bahwa akan lebih nyaman bagi pengguna jika permintaan tersebut berhenti berputar-putar dari satu tempat ke tempat lain.

Sementara itu, pengguna dengan hormat setuju untuk memberikan cuplikan gambar sistem. Ini adalah kabar baik: kemampuan untuk menguji sistem sendiri membuat pemecahan masalah menjadi lebih cepat, karena saya tidak lagi harus meminta pengguna untuk menjalankan perintah, mengirimkan saya hasilnya dan menganalisisnya, saya dapat melakukan semuanya sendiri!

Rekan-rekan saya mulai sedikit iri kepada saya. Saat makan siang kami mendiskusikan konversi tersebut, namun tidak ada yang tahu apa yang sedang terjadi. Untungnya, pengguna sendiri telah mengambil tindakan untuk mengurangi konsekuensinya dan tidak terburu-buru, jadi kami punya waktu untuk membedah masalahnya. Dan karena kami memiliki gambar, kami dapat menjalankan tes apa pun yang kami minati. Besar!

Mengambil langkah mundur

Salah satu pertanyaan wawancara paling populer untuk posisi insinyur sistem adalah: “Apa yang terjadi ketika Anda melakukan ping www.google.com? Pertanyaannya bagus, karena kandidat perlu menjelaskan semuanya mulai dari shell hingga ruang pengguna, hingga kernel sistem, dan kemudian ke jaringan. Saya tersenyum: terkadang pertanyaan wawancara ternyata berguna dalam kehidupan nyata...

Saya memutuskan untuk menerapkan pertanyaan HR ini pada masalah saat ini. Secara kasar, saat Anda mencoba menentukan nama DNS, hal berikut akan terjadi:

  1. Aplikasi memanggil perpustakaan sistem seperti libdns
  2. libdns memeriksa konfigurasi sistem ke server DNS mana yang harus dihubungi (dalam diagram ini adalah 169.254.169.254, server metadata)
  3. libdns menggunakan panggilan sistem untuk membuat soket UDP (SOKET_DGRAM) dan mengirim paket UDP dengan permintaan DNS di kedua arah
  4. Melalui antarmuka sysctl Anda dapat mengkonfigurasi tumpukan UDP di tingkat kernel
  5. Kernel berinteraksi dengan perangkat keras untuk mengirimkan paket melalui jaringan melalui antarmuka jaringan
  6. Hypervisor menangkap dan mengirimkan paket ke server metadata setelah kontak dengannya
  7. Server metadata, secara ajaib, menentukan nama DNS dan mengembalikan respons menggunakan metode yang sama

Kisah tentang paket DNS yang hilang dari dukungan teknis Google Cloud
Izinkan saya mengingatkan Anda hipotesis apa yang telah kami pertimbangkan:

Hipotesis: Perpustakaan rusak

  • Tes 1: jalankan strace di sistem, periksa apakah dig memanggil panggilan sistem yang benar
  • Hasil: Panggilan sistem yang benar dipanggil
  • Tes 2: menggunakan srapy untuk memeriksa apakah kita dapat menentukan nama dengan melewati perpustakaan sistem
  • Hasilnya: kita bisa
  • Tes 3: jalankan rpm –V pada paket libdns dan file perpustakaan md5sum
  • Hasilnya: kode perpustakaan benar-benar identik dengan kode di sistem operasi yang berfungsi
  • Tes 4: pasang image sistem root pengguna pada VM tanpa perilaku ini, jalankan chroot, lihat apakah DNS berfungsi
  • Hasilnya: DNS berfungsi dengan benar

Kesimpulan berdasarkan tes: masalahnya bukan di perpustakaan

Hipotesis: Ada kesalahan dalam pengaturan DNS

  • Tes 1: periksa tcpdump dan lihat apakah paket DNS terkirim dan dikembalikan dengan benar setelah menjalankan dig
  • Hasilnya: paket dikirimkan dengan benar
  • Tes 2: periksa kembali pada server /etc/nsswitch.conf и /etc/resolv.conf
  • Hasilnya: semuanya benar

Kesimpulan berdasarkan tes: masalahnya bukan pada konfigurasi DNS

Hipotesis: inti rusak

  • Tes: instal kernel baru, periksa tanda tangan, mulai ulang
  • Hasil: perilaku serupa

Kesimpulan berdasarkan tes: kernelnya tidak rusak

Hipotesis: perilaku jaringan pengguna (atau antarmuka jaringan hypervisor) yang salah

  • Tes 1: Periksa pengaturan firewall Anda
  • Hasilnya: firewall meneruskan paket DNS di host dan GCP
  • Tes 2: mencegat lalu lintas dan memantau kebenaran transmisi dan pengembalian permintaan DNS
  • Hasil: tcpdump mengonfirmasi bahwa host telah menerima paket pengembalian

Kesimpulan berdasarkan tes: masalahnya bukan di jaringan

Hipotesis: server metadata tidak berfungsi

  • Tes 1: periksa log server metadata untuk mencari anomali
  • Hasilnya: tidak ada anomali di log
  • Tes 2: Lewati server metadata melalui dig @8.8.8.8
  • Hasil: Resolusi rusak bahkan tanpa menggunakan server metadata

Kesimpulan berdasarkan tes: masalahnya bukan pada server metadata

Intinya: kami menguji semua subsistem kecuali pengaturan waktu proses!

Menyelami Pengaturan Waktu Proses Kernel

Untuk mengkonfigurasi lingkungan eksekusi kernel, Anda dapat menggunakan opsi baris perintah (grub) atau antarmuka sysctl. Saya melihat ke dalam /etc/sysctl.conf dan coba bayangkan, saya menemukan beberapa pengaturan khusus. Merasa seolah-olah saya telah meraih sesuatu, saya membuang semua pengaturan non-jaringan atau non-tcp, tetap menggunakan pengaturan gunung net.core. Lalu saya pergi ke tempat izin host berada di VM dan mulai menerapkan pengaturan satu per satu, satu demi satu, dengan VM yang rusak, hingga saya menemukan pelakunya:

net.core.rmem_default = 2147483647

Ini dia, konfigurasi pemecah DNS! Saya menemukan senjata pembunuh. Tapi mengapa ini terjadi? Saya masih membutuhkan motif.

Ukuran buffer paket DNS dasar dikonfigurasi melalui net.core.rmem_default. Nilai tipikalnya adalah sekitar 200KiB, namun jika server Anda menerima banyak paket DNS, Anda mungkin ingin menambah ukuran buffer. Jika buffer penuh saat paket baru tiba, misalnya karena aplikasi tidak memprosesnya dengan cukup cepat, Anda akan mulai kehilangan paket. Klien kami meningkatkan ukuran buffer dengan benar karena dia takut kehilangan data, karena dia menggunakan aplikasi untuk mengumpulkan metrik melalui paket DNS. Nilai yang dia tetapkan adalah nilai maksimum yang mungkin: 231-1 (jika diatur ke 231, kernel akan mengembalikan “INVALID ARGUMENT”).

Tiba-tiba saya menyadari mengapa nmap dan scapy bekerja dengan benar: mereka menggunakan soket mentah! Soket mentah berbeda dari soket biasa: soket ini melewati iptables, dan tidak di-buffer!

Namun mengapa "buffer terlalu besar" menimbulkan masalah? Ini jelas tidak berfungsi sebagaimana mestinya.

Pada titik ini saya dapat mereproduksi masalah pada banyak kernel dan beberapa distribusi. Masalah sudah muncul di kernel 3.x dan sekarang muncul juga di kernel 5.x.

Memang, saat startup

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

DNS berhenti bekerja.

Saya mulai mencari nilai kerja melalui algoritma pencarian biner sederhana dan menemukan bahwa sistem bekerja dengan 2147481343, tetapi bagi saya angka ini adalah kumpulan angka yang tidak ada artinya. Saya menyarankan klien untuk mencoba nomor ini, dan dia menjawab bahwa sistem berfungsi dengan google.com, tetapi masih memberikan kesalahan pada domain lain, jadi saya melanjutkan penyelidikan.

Saya telah menginstal jam tangan jatuh, alat yang seharusnya digunakan sebelumnya: alat ini menunjukkan dengan tepat di mana di dalam kernel sebuah paket berakhir. Pelakunya adalah fungsinya udp_queue_rcv_skb. Saya mengunduh sumber kernel dan menambahkan beberapa fungsi printk untuk melacak di mana tepatnya paket itu berakhir. Saya segera menemukan kondisi yang tepat if, dan hanya menatapnya selama beberapa waktu, karena pada saat itulah semuanya akhirnya menyatu menjadi satu gambaran utuh: 231-1, angka yang tidak berarti, domain yang tidak berfungsi... Itu adalah sepotong kode di __udp_enqueue_schedule_skb:

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

Harap dicatat:

  • rmem bertipe int
  • size bertipe u16 (unsigned int enam belas bit) dan menyimpan ukuran paket
  • sk->sk_rcybuf bertipe int dan menyimpan ukuran buffer yang, menurut definisi, sama dengan nilai in net.core.rmem_default

Ketika sk_rcvbuf mendekati 231, menjumlahkan ukuran paket dapat menghasilkan luapan bilangan bulat. Dan karena ini adalah int, nilainya menjadi negatif, sehingga kondisinya menjadi benar padahal seharusnya salah (Anda dapat membaca selengkapnya di link).

Kesalahan dapat diperbaiki dengan cara yang sepele: dengan casting unsigned int. Saya menerapkan perbaikan dan memulai ulang sistem dan DNS berfungsi kembali.

Rasa kemenangan

Saya meneruskan temuan saya ke klien dan mengirimkannya LKML tambalan kernel. Saya senang: setiap bagian dari teka-teki cocok satu sama lain, saya dapat menjelaskan dengan tepat mengapa kami mengamati apa yang kami amati, dan yang paling penting, kami dapat menemukan solusi untuk masalah tersebut berkat kerja tim kami!

Perlu diketahui bahwa kasus ini ternyata jarang terjadi, dan untungnya kami jarang menerima permintaan rumit seperti itu dari pengguna.

Kisah tentang paket DNS yang hilang dari dukungan teknis Google Cloud


Sumber: www.habr.com

Tambah komentar