Linux DNS 解析的真相

我嘗試根據網路上看到的這篇文章,標題是解析 Linux DNS 查詢,我會在此筆記一下,驗證一下是不是跟他描述的一樣如此的複雜。他裡面提到,作業系統裡面並沒有一個系統操作是可以直接從域名拿到 ip 的,實在令人有點意外。原本以為看完 google 搜尋結果的第一篇解釋就已經懂 DNS 了,但事情好像不是想像中那麼簡單。

首先介紹 strace ,這指令可以讓我們知道一個命令背後到底呼叫了什麼樣的 linux system call 。

man strace

可以利用 strace 分析常見的 pinghost 指令,再以這兩個指令為基礎,學習 linux 下的程式是如何解析 DNS 的。

ping

ping 指令的作用是用 icmp 協定去探測對面的機器有沒有回應。還可以順便反向解析 ip 對應的 domain 。類似於幫你用 dig -x 查 PTR record ,查到對應的 host name (di-in-f139.1e100.net)。

ping google.com
PING google.com (74.125.193.139) 56(84) bytes of data.
64 bytes from di-in-f139.1e100.net (74.125.193.139): icmp_seq=1 ttl=51 time=1.41 ms
64 bytes from ig-in-f139.1e100.net (74.125.193.139): icmp_seq=2 ttl=51 time=1.43 ms
64 bytes from di-in-f139.1e100.net (74.125.193.139): icmp_seq=3 ttl=51 time=1.55 ms
^C
--- google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 1.406/1.463/1.552/0.063 ms

接下來會用 strace 分析這個指令背後做了哪些系統操作。由於 strace 輸出很雜亂,全部放上來會很傷眼。我找出 open 相關的操作,看他實際上開了哪些檔案。可以看到下列的檔案被開啟。

  • /etc/nsswitch.conf
  • /etc/host.conf
  • /etc/resolv.conf
  • /etc/hosts
  • /etc/gai.conf

雖然看起來很多,但文章中只提到 nsswitch.conf ,hosts ,resolv.conf 三個下去細講。

sudo strace ping -c1 google.com 2>&1 | grep open
(...)
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/host.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5

既然裡面提到了,就查看一下 /etc/nsswitch.conf

cat /etc/nsswitch.conf
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

passwd:         files systemd
group:          files systemd
shadow:         files
gshadow:        files

hosts:          files dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis

裡面有一行是 hosts: files dns ,我們修改一下,把這行的 dns 字樣拿掉之後。就發現 ping google.com 壞掉不能用了。

ping google.com
ping: google.com: Name or service not known

但是 ping localhost 正常。

因此可推論,把 DNS 拿掉之後, /etc/hosts 的解析仍然正常,但送往 DNS resolver (設定在 /etc/resolv.conf) 的都會壞掉。

host

另一個指令 host 可以幫查詢 DNS 對應的紀錄。預設還會幫忙查 MX record ,十分貼心。

host google.com
google.com has address 74.125.193.100
google.com has address 74.125.193.101
google.com has address 74.125.193.102
google.com has address 74.125.193.113
google.com has address 74.125.193.138
google.com has address 74.125.193.139
google.com has IPv6 address 2a00:1450:400b:c01::8b
google.com has IPv6 address 2a00:1450:400b:c01::65
google.com has IPv6 address 2a00:1450:400b:c01::66
google.com has IPv6 address 2a00:1450:400b:c01::8a
google.com mail is handled by 10 smtp.google.com.

用同樣的操作來查看 strace ,結果只看到 resolv.conf ,似乎開的 /etc 下的檔案比較少。這次就沒有 /etc/nsswitch.conf 了。

sudo strace host google.com 2>&1 | grep open | grep /etc
(...)
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY) = 9

因此可判定 host 這個指令就與 /etc/nsswitch.conf 無關。目前可以得到以下的關係。

兩種不同的 DNS 解析途徑

/etc/resolv.conf 網路重置後自動回溯

不管如何改動 /etc/resolv.conf ,都會在系統 DNS 重置的時候被回朔並修改。我用 AWS EC2 ubuntu 20.04 可以用以下的指令重置。

sudo systemctl restart systemd-resolved

最後文章作者找到的原因是 dhclient 會在網路介面重啟動態生成 /etc/resolv.confdhclient 用來進行 DHCP 協定,協助取得 client ip 與 client resolver 。

Network Manager

這是一個 RedHat 推出的網路介面管理工具,裡面可以用一個叫做 dnsmasq 的東西代替原本的 resolv.conf 。dnsmasq 會作為一個 DNS cache server 跑在本機網路介面上的 port 53 。

Container (Docker)

若使用 docker image 要看出廠設定在 resolv.conf 的是哪台 DNS resolver 。如果設定成 127.0.0.1 ,可能會出事,因為 docker image 裡面應該沒有自建 DNS resolver 。我目前對 dockek network 一知半解,無法深入解釋。

Kubernetes

pod 內有四種方式做 DNS 解析。

    Default

    直接用 pod 所屬的 host 的解析方式。其實 Default 並非真正預設的解析方式。

    ClusterFirst

    Cluster First 才是真正預設的解析方式。可設定 stubDomains 與 upstreamNameservers 的對應 ip addresses,當符合某些 Domains 的時候往某台 DNS resolver 解析,其餘前往 upstreamNameservers 。實際上就是靠 Cluster 裡面的 KubeDNS (包含其中的 dnsmasq) 來做解析的選擇。

    在新一代的 Kubernetes 版本中, KubeDNS 被換掉成 CoreDNS 。

    ClusterFirstWithHostName

    跳過 docker 相關的 DNS 解析流程直接到 host 解析。我不知道這跟 default 差在哪裡。

    None

    不做任何 DNS 解析,除了設定在 pod 的 dnsConfig 裡面的。

    範例問題

    裡面提到一個 UDP 跟 TCP 有關的 DNS 問題。如果 DNS server 要回應超過 512 bytes 的封包,那就沒辦法用 UDP 回傳。 server 會回應跟 client 要求重傳一個 TCP 為底的 DNS 查詢。

    https://www.rfc-editor.org/rfc/rfc5966

    文章作者後來懷疑是 DNSMasq 的問題,連接多個網路介面時,會導致 DNSMasq 把回傳的 TCP 封包丟掉。

    看完的心得

    我感覺多懂了一些東西,但也覺得我相當的不熟悉 linux 系統的 DNS 解析元件。應該要學習更多 linux 作業系統的知識才比較能理解。或許也要補一些 docker 或 k8s 的網路知識。


    Posted

    in

    by

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *