Linux DNS 查詢剖析(第二部分)
在 Linux DNS 查詢剖析 - 第一部分 中,我介紹了:
nsswitch
/etc/hosts
/etc/resolv.conf
ping
與host
查詢方式對比
並且發現大多數程序選擇要查詢的 DNS 伺服器時會參考 /etc/resolv.conf
配置文件。
這種方式在 Linux 上比較普遍 1 。雖然我使用了特定的發行版 Ubuntu,但背後的原理與 Debian 甚至是那些基於 CentOS 的發行版有相通的地方;當然,與更低或更高的 Ubuntu 版本相比,差異還是存在的。
也就是說,接下來,你主機上的行為很可能與我描述的不一致。
在第二部分中,我將介紹 resolv.conf
的更新機制、systemctl restart networking
命令的運行機制 ,以及 dhclient
是如何參與其中。
1) 手動更新 /etc/resolv.conf
我們知道 /etc/resolv.conf
(有極大的可能性)被用到,故你自然可以通過該文件增加一個 nameserver
,那麼主機也將會(與已有的 nameserver
一起)使用新加入的 nameserver
吧?
你可以嘗試如下:
$ echo nameserver 10.10.10.10 >> /etc/resolv.conf
看上去新的 nameserver
已經加入:
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.0.2.3
search home
nameserver 10.10.10.10
但主機網路服務重啟後問題出現了:
$ systemctl restart networking
$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.0.2.3
search home
我們的 10.10.10.10
的 nameserver
不見了!
在上一篇文章中我們忽略了這一點,本文進行補充說明。
2) resolvconf
你在 /etc/resolv.conf
文件中看到 generated by resolvconf
片語了吧?這就是我們的線索。
如果深入研究 systemctl restart networking
命令,你會發現它做了很多事情,結束時調用了 /etc/network/if-up.d/000resolvconf
腳本。在該腳本中,可以發現一次對 resolvconf
命令的調用:
/sbin/resolvconf -a "${IFACE}.${ADDRFAM}"
稍微研究一下 man 手冊,發現-a
參數允許我們:
Add or overwrite the record IFACE.PROG then run the update scripts
if updating is enabled.
(增加或覆蓋 IFACE.PROG 記錄,如果開啟更新選項,則運行更新腳本)
故而也許我們可以直接調用該命令增加 namserver
:
echo 'nameserver 10.10.10.10' | /sbin/resolvconf -a enp0s8.inet
測試表明確實可以!
$ cat /etc/resolv.conf | grep nameserver
nameserver 10.0.2.3
nameserver 10.10.10.10
是否已經找到答案,這就是 /etc/resolv.conf
更新的邏輯?調用 resolvconf
將 nameserver
添加到某個地方的資料庫,然後(「如果配置了更新」,先不管具體什麼含義)更新 resolv.conf
文件。
並非如此。
$ systemctl restart networking
root@linuxdns1:/etc# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 10.0.2.3
search home
呃!(網路服務重啟後)新增的 nameserver
再次消失了。
可見,systemctl restart networking
不僅僅運行了 resolvconf
,還在其它地方獲取 nameserver
信息。具體是哪裡呢?
3) ifup/ifdown
繼續深入研究 systemctl restart networking
,發現它完成了一系列工作:
cat /lib/systemd/system/networking.service
[...]
[Service]
Type=oneshot
EnvironmentFile=-/etc/default/networking
ExecStartPre=-/bin/sh -c '[ "$CONFIGURE_INTERFACES" != "no" ] && [ -n "$(ifquery --read-environment --list --exclude=lo)" ] && udevadm settle'
ExecStart=/sbin/ifup -a --read-environment
ExecStop=/sbin/ifdown -a --read-environment --exclude=lo
[...]
首先,網路服務的重啟實質是運行一個 單觸發 的腳本,腳本包含如下命令:
/sbin/ifdown -a --read-environment --exclude=lo
/bin/sh -c '[ "$CONFIGURE_INTERFACES" != "no" ] && [ -n "$(ifquery --read-environment --list --exclude=lo)" ] && udevadm settle'
/sbin/ifup -a --read-environment
第一行使用 ifdown
關閉全部的網路介面,但 本地迴環 介面除外。 2
(LCTT 譯註:其實這是因為很快就又啟動了介面,間隔的時間沒有超過 TCP 連接的超時時間,有人在評論中也做了類似回復)
第二行用於確認系統已經完成關閉網路介面相關的全部工作,以便下一步使用 ifup
啟動介面。這也讓我們了解到,網路服務實質運行的就是 ifdown
和 ifup
。
文檔中沒有找到 --read-environment
參數的說明,該參數為 systemctl
正常工作所需。很多人以文檔不完善為由不喜歡 systemctl
。
很好。那麼 ifup
(和其成對出現的 ifdown
) 到底做了哪些工作呢?長話短說,它運行了 /etc/network/if-pre-up.d/
和 /etc/network/if-up.d/
目錄下的全部腳本;期間,這些腳本也可能會調用另外的腳本,依此類推。
其中一件工作就是運行了 dhclient
,但我還不完全確定具體的機理,也許 udev
參與其中。
4) dhclient
dhclient
是一個程序,用於與 DHCP 伺服器協商對應網路介面應該使用的 IP 地址的詳細信息。同時,它也可以獲取可用的 DNS 伺服器並將其替換到 /etc/resolv.conf
中。
讓我們開始跟蹤並模擬它的行為,但僅在我實驗虛擬機的 enp0s3
介面上。事先已經刪除 /etc/resolv.conf
文件中的 nameserver 配置:
$ sed -i '/nameserver.*/d' /run/resolvconf/resolv.conf
$ cat /etc/resolv.conf | grep nameserver
$ dhclient -r enp0s3 && dhclient -v enp0s3
Killed old client process
Internet Systems Consortium DHCP Client 4.3.3
Copyright 2004-2015 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/
Listening on LPF/enp0s8/08:00:27:1c:85:19
Sending on LPF/enp0s8/08:00:27:1c:85:19
Sending on Socket/fallback
DHCPDISCOVER on enp0s8 to 255.255.255.255 port 67 interval 3 (xid=0xf2f2513e)
DHCPREQUEST of 172.28.128.3 on enp0s8 to 255.255.255.255 port 67 (xid=0x3e51f2f2)
DHCPOFFER of 172.28.128.3 from 172.28.128.2
DHCPACK of 172.28.128.3 from 172.28.128.2
bound to 172.28.128.3 -- renewal in 519 seconds.
$ cat /etc/resolv.conf | grep nameserver
nameserver 10.0.2.3
可見這就是 nameserver
的來源。
但稍等一下,命令中的 /run/resolvconf/resolv.conf
是哪個文件,不應該是 /etc/resolv.conf
嗎?
事實上,/etc/resolv.conf
並不一定只是一個普通文本文件。
在我的虛擬機上,它是一個軟鏈接,指向位於 /run/resolvconf
目錄下的「真實文件」。這也暗示了我們,該文件是在系統啟動時生成的;同時,這也是該文件注釋告訴我們不要直接修改該文件的原因。
(LCTT 譯註:在 CentOS 7 中,沒有 resolvconf
命令,/etc/resolv.conf
也不是軟鏈接)
假如上面命令中 sed
命令直接處理 /etc/resolv.conf
文件,效果是不同的,會有警告消息告知待操作的文件不能是軟鏈接(sed -i
無法很好的處理軟鏈接,它只會創建一個新文件)。
(LCTT 譯註:CentOS 7 測試時,sed -i
命令操作軟鏈接並沒有警告,但確實創建了新文件取代軟鏈接)
如果你繼續深入查看配置文件 /etc/dhcp/dhclient.conf
的 supersede
部分,你會發現 dhclient
可以覆蓋 DHCP 提供的 DNS 伺服器。
(大致)準確的關係圖
第二部分的結束語
第二部分到此結束。信不信由你,這是一個某種程度上簡化的流程版本,但我盡量保留重要和值得了解的部分,讓你不會感到無趣。大部分內容都是圍繞實際腳本的運行展開的。
但我們的工作還沒有結束,在第三部分,我們會介紹這些之上的更多層次。
讓我們簡要列出我們已經介紹過的內容:
nsswitch
/etc/hosts
/etc/resolv.conf
/run/resolvconf/resolv.conf
systemd
和網路服務ifup
和ifdown
dhclient
resolvconf
via: https://zwischenzugs.com/2018/06/18/anatomy-of-a-linux-dns-lookup-part-ii/
作者:ZWISCHENZUGS
譯者:pinewall
校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive