天天看點

永遠不要使用二級域名作為 Linux Hostname

作者:區塊軟體開發

我們常常使用 Linux 的 Hostname 來辨別一台伺服器。Phil KarIton 曾說過:

計算機科學隻存在兩個難題:命名和緩存失效。

如何想出一個有意義、便于運維,甚至有個人風格(對于個人伺服器來說)的 Hostname 也是一件難事。曾有一個有趣的 RFC —— RFC 1178: Choosing a Name for Your Computer 給出了一些關于命名 Hostname 的建議。

在這份 RFC 中,曾經提到了「不要使用域名或類似域名的東西作為主機名」:

Avoid domain names.

For technical reasons, domain names should be avoided. In particular, name resolution of non-absolute hostnames is problematic. Resolvers will check names against domains before checking them against hostnames. But we have seen instances of mailers that refuse to treat single token names as domains. For example, assume that you mail to “[email protected]” from yale.edu. Depending upon the implementation, the mail may go to rutgers.edu or rutgers.yale.edu (assuming both exist).

Avoid domain-like names.

Domain names are either organizational (e.g., cia.gov) or geographical (e.g., dallas.tx.us). Using anything like these tends to imply some connection. For example, the name “tahiti” sounds like it means you are located there. This is confusing if it is really somewhere else (e.g., “tahiti.cia.gov is located in Langley, Virginia? I thought it was the CIA’s Tahiti office!”). If it really is located there, the name implies that it is the only computer here. If this isn’t wrong now, it inevitably will be There are some organizational and geographical names that work fine. These are exactly the ones that do not function well as domain names. For example, amorphous names such as rivers, mythological places and other impossibilities are very suitable. (“earth” is not yet a domain name.)

使用域名作為 Hostname 不僅是一個技術錯誤,還會帶來潛在的資訊安全問題,尤其是對于使用二級域名(例如 example.com)來說。就在剛剛,我碰到了一個生動的例子。

問題描述

在我的伺服器上,我使用 josephcz.xyz 作為主機名。我剛剛調整了我的 Nginx 設定,以增加新的 Web 服務。但是,似乎我的 DNS 解析并沒有生效。當我使用 curl 通路我的新設定的主機名時,卻發現解析到了我從未見過的位址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
           
$ curl -v api-v2023.josephcz.xyz
*   Trying 52.9.36.254:80...
* TCP_NODELAY set
* Connected to api-v2023.josephcz.xyz (52.9.36.254) port 80 (
> GET / HTTP/1.1
> Host: api-v2023.josephcz.xyz
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Server: awselb/2.0
< Date: Thu, 15 Jun 2023 01:00:25 GMT
< Content-Type: text/html
< Content-Length: 134
< Connection: keep-alive
< Location: https://api-v2023.josephcz.xyz:443/
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
</body>
</html>
* Connection 
           

當我看到我的域名被解析到未預期的 IP 位址時,我的第一反應是 DNS 劫持。然而我的伺服器位于香港,使用了 Google DNS 和 Cloudflare DNS 作為 DNS 伺服器。當我使用 nslookup 或 dig 指令查詢域名時,得到的結果卻是正确的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
           
$ dig api-v2023.josephcz.xyz

; <<>> DiG 9.16.1-Ubuntu <<>> api-v2023.josephcz.xyz
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61092
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;api-v2023.josephcz.xyz.		IN	A

;; AUTHORITY SECTION:
josephcz.xyz.		180	IN	SOA	ns3.dnsv2.com. level3dnsadmin.dnspod.com. 1686782943 3600 180 1209600 180

;; Query time: 211 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
;; WHEN: Thu Jun 15 09:02:51 HKT 2023
;; MSG SIZE  rcvd: 122
           

事實上,問題的根源在于 Hostname 的設定。

Hostname 和 Search Domain

在安裝 Linux 的時候,發行版會讓我們輸入 Hostname。如果輸入的是一個帶點号的域名(如 webserver.example.com),安裝程式會自動将其拆分為 Hostname 和 Search Domain。Hostname 的部分為 webserver,Search Domain 的部分為 example.com。

Search Domain 是怎樣工作的

假如您有兩台将 Search Domain 配置為 example.com,伺服器,假設它們的 Hostname 為:

  • webserver-1
  • webserver-2

并且,在 DNS 伺服器上,配置了如下的解析:

  • webserver-1.example.com: 192.168.1.1
  • webserver-2.example.com: 192.168.1.2

當你在 webserver-1 上輸入域名 webserver-2 通路各種服務時,由于配置了 example.com 為 Search Domain,Linux 會自動解析為 webserver-2.example.com。具體地說,這個過程是這樣的:

  1. 檢查 /etc/hosts 檔案是否存在 webserver-2 的記錄,如果存在,直接傳回對應的 IP 位址;
  2. 系統向 DNS 伺服器查詢 webserver-2 的 IP 位址;
  3. DNS 伺服器會将 webserver-2 了解為一個類似 .com、.net 的根域名。由于不存在這個根域名,DNS 伺服器傳回 NXDOMAIN 錯誤;
  4. 由于 NXDOMAIN 錯誤,系統将 webserver-2 加上 Search Domain 拼接為 webserver-2.example.com,再次向 DNS 伺服器查詢;
  5. DNS 伺服器傳回 webserver-2.example.com 的 IP 位址 192.168.1.2。

通過使用 Search Domain,我們可以減少輸入、記憶域名的工作量,對于運維工作提供了很大的便利。

當 Hostname 是一個 FQDN 時

當 Hostname 是一個 FQDN 時,其解析過程也是類似的。同樣以在 webserver-1 上解析 webserver-2 為例:

  1. 檢查 /etc/hosts 檔案是否存在 webserver-2 的記錄,如果存在,直接傳回對應的 IP 位址;
  2. 系統向 DNS 伺服器查詢 webserver-2 的 IP 位址;
  3. DNS 伺服器會将 webserver-2 了解為一個類似 .com、.net 的根域名。由于不存在這個根域名,DNS 伺服器傳回 NXDOMAIN 錯誤;
  4. 由于 NXDOMAIN 錯誤,系統移除 Hostname 的第一個分段,并将其替換為需要查詢的域名。即将 webserver-1.example.com 移除 webserver-1 變成 example.com,再拼接上需要查詢的域名,向 DNS 伺服器查詢 webserver-2.example.com 的 IP 位址;
  5. DNS 伺服器傳回 webserver-2.example.com 的 IP 位址 192.168.1.2。

如果我們使用二級域名作為 Hostname

在以上的例子中,伺服器的 Hostname 都是一個三級域名。倘若我們将伺服器的 Hostname 切換為二級域名,例如 example.com 時,會發生什麼呢?在這裡我們假設一個沒有被任何人注冊的域名,例如 some-not-registered-domain.com;并假設 com.com 這一域名被惡意人士注冊。

接下來,我們使用 ping 指令,加上需要解析的域名,看看實際通路的域名時哪一個:

永遠不要使用二級域名作為 Linux Hostname

是的,在使用二級域名作為 Hostname 的情況下,當 ping 一個尚未注冊的域名 some-not-registered-domain.com 時,由于 Hostname 和 Search Domain 的配置,會自動 fallback 到 some-not-registered-domain.com.com 上!遺憾的是,不管是否使用 Search Domain,這個問題都存在——而将 Hostanme 設定為 FQDN 讓這個問題更加隐蔽了。

這個問題不僅影響了 ping、curl,也影響了幾乎所有使用 glibc 的程式:OpenSSL、MySQL、Nginx、Apache……等等。如果它們的某個功能需要解析域名,則都會有同樣的問題。

未被注冊的域名值得警惕,但是這一問題如果發生在你自己的域名上呢?

如果我們使用已經被注冊的域名作為 Hostname

回到我遇到的問題上來。我剛剛設定了 api-v2023.josephcz.xyz 這一域名的解析。而 DNS 解析的生效需要時間,在遞歸 DNS 未擷取到生效的記錄的情況下,會傳回一個空記錄。而空記錄和 NXDOMAIN 錯誤一樣,會觸發 Search Domain 的 fallback 機制。是以,伺服器上對 api-v2023.josephcz.xyz 全部被發送到了 api-v2023.josephcz.xyz.xyz。

使用 tcpdump -i ens3 -nt -s 500 port domain 指令可以清楚地看到這一過程:

1
2
3
4
5
6
7
           
IP 10.0.0.12.57943 > 1.1.1.1.53: 27244+ A? api-v2023.josephcz.xyz. (35)
IP 10.0.0.12.57943 > 1.1.1.1.53: 19043+ AAAA? api-v2023.josephcz.xyz. (35)
IP 1.1.1.1.53 > 10.0.0.12.57943: 19043 0/1/0 (106)
IP 1.1.1.1.53 > 10.0.0.12.57943: 27244 0/1/0 (106)
IP 10.0.0.12.46092 > 1.1.1.1.53: 51629+ A? api-v2023.josephcz.xyz.xyz. (39)
IP 10.0.0.12.46092 > 1.1.1.1.53: 61104+ AAAA? api-v2023.josephcz.xyz.xyz. (39)
IP 1.1.1.1.53 > 10.0.0.12.46092: 51629 3/0/0 CNAME xyz.xyz., A 52.9.36.254, A 54.241.183.58 (85)
           

好在 XYZ 域名的注冊局保留了 XYZ.XYZ。但是如果換成解析其他域名,例如 some-api-host.aws 或者 pki.goog,而相關域名的注冊局又忘記保留了 aws、goog 之類的域名呢?

結論

在設定 Linux 的 Hostname 時,永遠不要使用一個二級域名——無論是将 Hostname 設定為 FQDN 還是配置 Search Domain。永遠使用一個三級或以上的、受你自己控制的域名作為 Hostname。

此外,啟用強制性的 TLS 并正确部署證書信任也可以在一定程度上緩解這一問題——由于對方無法獲得正确的 TLS 證書,即使解析被 fallback 到惡意攻擊者的域名上,也難以竊取你的機密資訊。

from https://josephcz.xyz/technology/linux/never-use-2ld-as-hostname/

繼續閱讀