將所有傳出 DNS 查詢重定向到位於 127.0.0.1:53 的本地存根解析器
我正在嘗試將我的 Linux 機器上的所有傳出 DNS 查詢重定向到我的本地記憶體存根解析器(未綁定)。
iptables -t nat -A OUTPUT -p tcp --dport 53 -j DNAT --to 1.1.1.1:53 iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to 1.1.1.1:53 iptables -t nat -A POSTROUTING -j MASQUERADE
當我使用上述規則時,所有傳出的 DNS 查詢都會被攔截並重定向到位於 1.1.1.1 的 DNS 伺服器
但是,如果我將 ‘1.1.1.1’ 替換為 ‘127.0.0.1’,所有 DNS 查詢都會失敗並且不會定向到我的本地存根解析器。
我確實通過了以下 sysctl 參數
sysctl -w net.ipv4.conf.eth0.route_localnet=1
但我的問題還是一樣。任何指針?
如果使用
strace
, 和nc
/進行調試,會變得很清楚socat
的是nat/POSTROUTING
’sMASQUERADE
並沒有改變最初選擇的用於外出的地址。可能是因為它仍然被認為是要“路由”到的本地地址,lo
所以不需要更改:該MASQUERADE
規則在這裡無效。不管怎樣,事情就是這樣。因此,在回复UDP查詢時,伺服器實際上連接回發送數據的源,現在用作目標。自然會選擇最佳源用於此目的地,即相同的本地地址,而不是127.0.0.1。因此,如果在後面加上 ,則會發生以下情況
conntrack -E
,例如本地 IP 為 192.0.2.2,目的地為 198.51.100.1 UDP 埠 53:[NEW] udp 17 30 src=192.0.2.2 dst=198.51.100.1 sport=40037 dport=53 [UNREPLIED] src=127.0.0.1 dst=192.0.2.2 sport=53 dport=40037 [NEW] udp 17 30 src=192.0.2.2 dst=192.0.2.2 sport=53 dport=40037 [UNREPLIED] src=172.16.0.22 dst=172.16.0.22 sport=40037 dport=53
回復與初始查詢無關(因為源 IP 不是 127.0.0.1),因此 conntrack 將其作為第二個流程處理。同時,客戶端將其 UDP 套接字置於連接模式,這意味著從錯誤的源 IP(即使埠正確)接收到的 UDP 數據包將被拒絕,並且伺服器接收到 ICMP 錯誤(這可以通過 來見證
tcpdump -i lo
)。更正很簡單:不要使用
MASQUERADE
butSNAT
。當然,它現在必須專門用於這個特定的流程(你不想把SNAT
所有的東西都放到 127.0.0.1),所以MASQUERADE
用這個代替這一行:iptables -t nat -A POSTROUTING -p udp --dport 53 -j SNAT --to-source 127.0.0.1
使用更正的流程後,本地伺服器現在使用 conntrack 的預期地址進行回复,該地址現在將其與之前的流程相關聯並正確地對其進行 de-SNAT:
[NEW] udp 17 30 src=192.0.2.2 dst=198.51.100.1 sport=38871 dport=53 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=53 dport=38871 [UPDATE] udp 17 30 src=192.0.2.2 dst=198.51.100.1 sport=38871 dport=53 src=127.0.0.1 dst=127.0.0.1 sport=53 dport=38871
客戶端收到預期的源 198.51.100.1 並且所有工作都按預期進行。
TCP不會遭受相同的結果,因為一旦在 192.0.2.2 和 127.0.0.1 之間建立了連接,回复就在同一個已建立的連接內,它不像 UDP 那樣是新連接,因此已經有了預期的來源和由 conntrack 正確處理。最好還是添加這個以保持一致性:
iptables -t nat -A POSTROUTING -p tcp --dport 53 -j SNAT --to-source 127.0.0.1
兩個注意事項:
- 對於您的特定情況,
route_localnet
不需要,因為所有數據包都是本地的並且保持在lo
. 相反:轉發發送到 127.0.0.1 的其他數據包將需要它(以及其他技巧)。- 如果您的 DNS 伺服器也是向外部發送查詢的 DNS 客戶端(遞歸 DNS 伺服器就是這種情況),或者它自己的查詢將被重新路由到自己創建一個循環,您可能需要額外的例外規則。通常通過讓伺服器與特定使用者一起執行並使用 iptables 的
-m owner
匹配來解決。類似於在每組規則(innat/OUTPUT
和nat/POSTROUTING
)之前插入這樣的東西:iptables -t nat -I .... -m owner --uid-owner unbound -j RETURN