Iptables

將所有傳出 DNS 查詢重定向到位於 127.0.0.1:53 的本地存根解析器

  • April 5, 2019

我正在嘗試將我的 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)。

更正很簡單:不要使用MASQUERADEbut SNAT。當然,它現在必須專門用於這個特定的流程(你不想把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/OUTPUTnat/POSTROUTING)之前插入這樣的東西:
iptables -t nat -I .... -m owner --uid-owner unbound -j RETURN

引用自:https://unix.stackexchange.com/questions/510781