Geçenlerde bu yazımızda özellikle docker container sistemlerde yaygın olarak kullanılan Linux dağıtımı olan Alpine dağıtımında varsayılan sistem C kütüphanesi olarak kullanılan Musl kütüphanesini, DNS ile ilgili bazı konularda nasıl davranıyor diye incelemiştik.

Bu yazıda biraz DNS protokolünü, biraz da yine Musl ve Alpine hangi konularda bize sorun çıkarabilir bunları inceleyelim. Tabi burada asıl amacımız, üzerinde uygulama çalıştırdığınız container ve sistem kütüphanesi geliştirdiğimiz sistemin davranışını farkında olmasanız da etkilediği için, hangi konularda dikkatli olmamız gerekiyor önceden bilip hazırlıklı olmak.

Sorunumuz

Diyelim ki uygulamamızı, büyük bir kubernetes ya da docker swarm cluster içinde container/pod olarak deploy ettik. Bu uygulama da diğer iç ya da dış servislerle haberleşmek için önce isim çözümlemesi yapacak ve aşağıdaki gibi bir istek gönderecek.

Kullandığımız dış servisi aslında hem local ortamımızda hem de test ortamında kontrol etmiştik, çözümleme düzgün yapılıyordu.

cihataltuntas@local% ping toomany.ddstreet.org
PING toomany.ddstreet.org (10.254.200.220): 56 data bytes
..
--- toomany.ddstreet.org ping statistics ---
2 packets transmitted, 0 packets received, 100.0% packet loss

cihataltuntas@local % docker run --dns=8.8.8.8 alpine:3.18 ping toomany.ddstreet.org
PING toomany.ddstreet.org (10.254.200.239): 56 data bytes
..
--- toomany.ddstreet.org ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

Yukarıdaki çıktılardan görüldüğü gibi direk local bilgisayarımdan ve bir de sonra versiyon Alpine container üzerinden aynı adrese ping isteği gönderdim, ikisi de başarılı. Peki aynı isteği mesela, çok yakın zamana kadar aktif kullanılan bizim de production ortamlarda kullandığımız Alpine 3.17 versiyonuna gönderelim bakalım ne oluyor?

cihataltuntas@local% docker run --dns=8.8.8.8 alpine:3.17 ping toomany.ddstreet.org
ping: bad address 'toomany.ddstreet.org'

İlginç bir şekilde aslında DNS server olarak bir sorun olmamasına rağmen, özellikle google dns sunucularını belirttim, adres de doğru olmasına rağmen 3.17 versiyonunda hatalı adres olarak çıktı verdi.

DNS ve TCP İlişkisi

Uzun süre DNS protokolünün UDP ile çalıştığını sanırdık, ta ki bir sorun yaşayıp her zaman öyle olmadığını öğrenene kadar. Bende son zamanlara kadar TCP ile bir ilişkisinin olmadığını sanıyordum fakat durum öyle değilmiş. Bir DNS sorgusu yaptığınızda eğer dönen cevap belirli bir boyutun (512 byte) üzerinde ise, RFC-5966 protokol standartları gereği geri kalan iletişim TCP üzerinden devam etmek zorunda.

Dönen cevap içinde aslında bu bir flag olarak istemciye iletiliyor ve bunu gören istemci bu sefer TCP üzerinden aynı isteği tekrar yapıyor. Ne demek istediğimi yukarıdaki isteği tekrar gönderip, Wireshark üzerinden dönen cevabı inceleyerek anlatmaya çalışayım.

cihataltuntas@local% nslookup toomany.ddstreet.org
;; Truncated, retrying in TCP mode.
Server:         172.16.33.1
Address:        172.16.33.1#53

Non-authoritative answer:
Name:   toomany.ddstreet.org
Address: 10.254.200.233
Name:   toomany.ddstreet.org
Address: 10.254.200.216
Name:   toomany.ddstreet.org
Address: 10.254.200.221
Name:   toomany.ddstreet.org
Address: 10.254.200.218
Name:   toomany.ddstreet.org
Address: 10.254.200.227
Name:   toomany.ddstreet.org
Address: 10.254.200.215
Name:   toomany.ddstreet.org
Address: 10.254.200.210
Name:   toomany.ddstreet.org
Address: 10.254.200.209
Name:   toomany.ddstreet.org
Address: 10.254.200.206
Name:   toomany.ddstreet.org
Address: 10.254.200.205
Name:   toomany.ddstreet.org
Address: 10.254.200.230
Name:   toomany.ddstreet.org
Address: 10.254.200.225
...
...

Bu isteği nslookup ile kendi ortamımdan yaptım , ilk dikkatinizi çeken şey muhtemelen çok fazla çözümleme yapmak istediğimiz adresin çok fazla IP adresine sahip olması. Sonucu bir de paket trafiğine bakarak değerlendirelim.

Capture 1

Görüldüğü gibi ilk istekler UDP üzerinden gönderilmiş fakat sonrasında sunucudan gelen cevapta gözüken truncated değeri görülünce, DNS sorgulaması yapan program, iletişimi TCP üzerinden tekrar açıp tekrar sorgulama yapıp cevap almış.

Alpine 3.17 ve Öncesindeki Sorun

Yukarıda protokolün hangi durumda UDP hangi durumda TCP ile haberleştiğini gördük, işte bu da aslında 3.17 versiyonunda çalışmamasının asıl sebebi. Çok yakın zamana kadar Musl Libc DNS over TCP kısmını desteklemiyordu, bundan dolayı Alpine dağıtımında sistem kütüphanesinin DNS çözümlemesini kullanan her program, ya da kendi yazdığınız kod bu tarz bir durumda hata veriyordu.

Bu destek çok yakın zamana kadar hem Musl hem de Alpine üzerinde yoktu, geçen sene Alpine 3.18 ile birlikte bu sorun çözülmüş oldu, fakat eski sürümlerde devam ettiği için sorunu yaşamamak için yeni bir sürüm Alpine kullanmanız gerekiyor.

Aynı sorunu eski versiyon Alpine üzerinden gönderdiğimizde nasıl bir paket trafiği oluyor bir de onu inceleyelim.

cihataltuntas@local% docker run --dns=8.8.8.8 alpine:3.17 ping toomany.ddstreet.org
ping: bad address 'toomany.ddstreet.org'

Capture 2

Paket trafiğinden de görüldüğü gibi truncated cevabı yani 512 byte değerinden büyük bir değer dönüldüğünü belirten bayrak 1 olarak gönderilmesine rağmen o versiyonda kullanılan sistem kütüphanesi bunu dikkate almadığı için, TCP geçişi yapılmıyor ve çözümleme başarısız oluyor.

Nslookup Garipliği

Son olarak bu testleri yaparken genelde sistem kütüphanesinin DNS çözümleme mekanizmasını kullandığını bildiğim ping, getent kullanıyordum. Genelde nslookup, dig gibi bu işe özel olarak yazılmış araçlar sistem kütüphanesini kullanmayıp kendi çözümlemesini kendileri yapabiliyor. Yani eski versiyon bir alpine bile olsa, bind-tools paketi içinde gelen nslookup aracını kursanız Musl TCP desteği olmasa bile kendi işini kendi yaptığı için sorun olmayacak, ama tabi bu yazdığınız kodun çalışacağı anlamına gelmiyor çünkü çoğunlukla sistem kütüphanesini kullanacaklar.

cihataltuntas@local% docker run -it --dns=8.8.8.8  alpine:3.17 sh
/ # apk add bind-tools
fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.17/community/x86_64/APKINDEX.tar.gz
(1/13) Installing fstrm (0.6.1-r1)
...
...
Executing busybox-1.35.0-r29.trigger
OK: 14 MiB in 28 packages
/ # nslookup toomany.ddstreet.org
;; Truncated, retrying in TCP mode.
Server:         8.8.8.8
Address:        8.8.8.8#53

Non-authoritative answer:
Name:   toomany.ddstreet.org
Address: 10.254.200.206
Name:   toomany.ddstreet.org
Address: 10.254.200.230
Name:   toomany.ddstreet.org
Address: 10.254.200.236
Name:   toomany.ddstreet.org
Address: 10.254.200.219
Name:   toomany.ddstreet.org
...

Yukarıda eski versiyon üzerine bu paket ile gelen nslookup kurulumu yaptım, ping ile çözemediği adresi bu sefer çözdü, ama bu zaten beklediğim bir durumdu takıldığım nokta son versiyon Alpine üzerinde ping ile çalışırken, varsayılan olarak yüklü gelen nslookup ile çalışmaması beni şaşırttı. Benim düşünceme göre ping sistem kütüphanesini kullanıyorsa o da aynısı kullanmalı ve TCP geçişi yapıp çözebilmeliydi ama öyle olmadı.

cihataltuntas@local% docker run --dns=8.8.8.8  alpine:latest nslookup toomany.ddstreet.org && ping toomany.ddstreet.org
Server:         8.8.8.8
Address:        8.8.8.8:53

Non-authoritative answer:

Non-authoritative answer:

PING toomany.ddstreet.org (10.254.200.236): 56 data bytes

Sorun İçinde Sorun

Yukarıda son versiyon üzerinde gelen nslookup ile sorgulama yaptığımda boş cevap geldiğini görüyorsunuz aslında aynı sistem üzerinde ping ile yapıldığında IP çözülebiliyor.

cihataltuntas@local% docker run -it --dns=8.8.8.8  alpine:latest sh
/ # nslookup
BusyBox v1.36.1 (2023-11-07 18:53:09 UTC) multi-call binary.
Usage: nslookup [-type=QUERY_TYPE] [-debug] HOST [DNS_SERVER]
...

/ # ping
BusyBox v1.36.1 (2023-11-07 18:53:09 UTC) multi-call binary.
Usage: ping [OPTIONS] HOST
...

Versiyonlarını kontrol ettim, ikisi de BusyBox kullanıyor. BusyBox özellikle gömülü ya da küçük boyuta sahip olması istenen sistemler için Userland yani çekirdek de değil de kullanıcı tarafında çalışan, ls,mv,cp... gibi bildiğiniz klasik araçların tek bir binary içinde toplanmış hali diyebiliriz. Bunu aşağıda gibi de anlayabilirsiniz.

cihataltuntas@local% docker run --dns=8.8.8.8  alpine:latest ls -lah /usr/bin/
total 200K
drwxr-xr-x    2 root     root        4.0K Jan 26 17:53 .
drwxr-xr-x    7 root     root        4.0K Jan 26 17:53 ..
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 [ -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 [[ -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 awk -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 basename -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 bc -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 beep -> /bin/busybox
lrwxrwxrwx    1 root     root          12 Jan 26 17:53 blkdiscard -> /bin/busybox
....
....

Bütün araçlar aslında tek bir programa sembolik link oluşturulmuş, aslında bunlar ayrı programlar değil tek bir busybox programının parçası. Kafa karıştırıcı olan ise, eğer varsayılan ping ve nslookup aynı programın parçası ise ve bu program da musl libc kullanması için derlendiyse neden biri çözerken diğeri çözemiyor?

Sorunu anlamak için Busybox kaynak kodunda nslookup kısmına bakayım dedim. Orada üstlerde bulunan yorum satırları dikkatimi çekti hemen.

//config:config FEATURE_NSLOOKUP_BIG
//config: bool "Use internal resolver code instead of libc"
//config: depends on NSLOOKUP
//config: default y

Yorumda gördüğüme şaşırdım diyebilirim, kendi internal resolver mantığını kullan ve kullanma diye iki seçenek var ve varsayılan olarak kendi mantığını kullanıyor yani, sistem kütüphanesinden konfigürasyon yapılmadıysa bu konuda destek almıyor. Belki bu konfigürasyon Alpine tarafında yapılmıştır dedim ve buradan kontrol ettim. Tahmin ettiğiniz gibi maalesef Alpine da bu değer CONFIG_FEATURE_NSLOOKUP_BIG=y olarak verilmiş bu da BusyBox nslookup kendi çözümleme kodunu işletiyor anlamına geliyor.

Bu bana bir tutarsızlık gibi geldi, en azından ping içinde kendi çözümleme yapmayıp sistem çözümlemesi kullanıyorsa, BusyBox içinde gelen basit nslookup aracının da aynısını yapmasın beklerdim, en kötü Alpine bu şekilde build etse daha doğru olurdu diye düşünüyorum farklı bir durumu gözden kaçırmıyorsam.


<
Previous Post
Shell Hileleri - Vim ile Özel Karakterler
>
Next Post
Ascii Tablosu Üzerinde Bit Manipülasyonları