Iptables

帶有 nat 和埠轉發的 iptable 上的奇怪行為

  • June 1, 2019

我在多個數據中心託管了幾台專用伺服器,我想將郵件(pop3imapsmtp及其 TLS/SSL 變體)服務從一台伺服器遷移到另一台伺服器。

為此,我打算在新伺服器上臨時安裝一個NAT路由到舊伺服器,以處理 DNS 傳播時間。

所以我定義了以下IPTABLES規則:

iptables -t nat -A PREROUTING -p tcp -m tcp --dport 25  -j DNAT --to-destination <my_remote_ip>:8025
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 110 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 143 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 465 -j DNAT --to-destination <my_remote_ip>:8465
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 587 -j DNAT --to-destination <my_remote_ip>:8587
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 993 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A PREROUTING -p tcp -m tcp --dport 995 -j DNAT --to-destination <my_remote_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 110  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 143  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 993  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 995  -j SNAT --to-source <my_local_ip>
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8025 -j SNAT --to-source <my_local_ip>:25
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8465 -j SNAT --to-source <my_local_ip>:465
iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8587 -j SNAT --to-source <my_local_ip>:587

iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 110  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 143  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 993  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 995  -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8025 -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8465 -j ACCEPT
iptables -A FORWARD -d <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --dport 8587 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 110  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 143  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 993  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 995  -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8025 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8465 -j ACCEPT
iptables -A FORWARD -s <my_remote_ip> -i eth0 -o eth0 -p tcp -m tcp --sport 8587 -j ACCEPT

(實際上簡化了,實際上這對於 IPv4 和 IPV6 是重複的,並且在某些伺服器上,介面可能與 eth0 不同……當然我弄亂了實際的 IP 地址)

您可能會注意到郵件服務只是經過 NAT 的,但與 SMTP 相關的服務也有埠轉換,與反向轉換或目標伺服器上這些埠的特定偵聽相關聯。

這有一個令人信服的理由:我的託管服務提供商使用監控傳出 SMTP 連接來檢測和阻止託管在他們託管的所有伺服器上的垃圾郵件。但是,如果我將傳入的連接到 SMTP 埠轉發到我的另一台伺服器,傳入的垃圾郵件在它有機會被過濾*(在目標伺服器上*)之前就變成了傳出的垃圾郵件 (從數據中心的角度來看) ,結果是我的託管服務提供商立即阻止了 NAT’ting 伺服器。

所以我還必須翻譯埠號才能轉發這些連接。

傳入數據包和傳出(經過 NAT 的)數據包使用相同的介面*(因為這些伺服器只有一個網路介面)*。

實際上這或多或少有效,除了埠轉發的連接(只有這些,埠 110、143 等沒有問題)有一個奇怪的行為:它們在我第一次使用它們時工作,但如果我斷開連接並立即重新連接,轉發不再起作用,我必須等待大約 1 到 3 分鐘才能再次連接。

這似乎與 IP 地址有關,與埠號無關:即使之前的連接在埠 110(pop3,未轉發埠)上,我也必須等待相同的延遲才能連接到埠 25。

我已經在幾台伺服器上驗證了這一點,它們都是Linux Debian WheezyJessieStretch,並且在IPv4IPv6 上(除了不能 NAT IPv6 的 Wheezy 上)。………… 是的,我知道 Wheezy 現在老了,這就是我遷移的原因。

所有 IP 地址都是完全靜態的。

是的,我已將**/proc/sys/net/ipv4/ip_forward**(它相當於 IPv6)設置為1

我使用telnet測試連接,並使用tcpdump檢查轉發。使用後者,我可以檢查轉發是否真的沒有完成,並且阻止傳入連接的不是目標伺服器。

請有人幫我找出這 1-3 分鐘阻塞延遲的原因以及我該如何解決?

銀杏

您的問題與 linux 連接跟踪器的特殊性有關。

快速回答:您無法避免配置中的這種延遲。避免此問題的唯一方法是使用選項中-j SNAT沒有埠號規範--to-source

還有一個小技巧,對您幫助不大 - 在j SNAT埠範圍內使用單個埠號。它可以讓您建立多個連接。同時連接數是該範圍內的埠數。規則看起來像:

iptables -t nat -A POSTROUTING -d <my_remote_ip> -p tcp -m tcp --dport 8025 -j SNAT --to-source <my_local_ip>:10025-10125

如果您想要血腥的細節,我可以擴展答案。

更新

要了解細節,您應該有一些背景知識。"Linux kernel networking: implementation and theory"由拉米·羅森閱讀。主要是需要本書中的“Chapter 9. Netfilter. Connection tracker”。

當數據包通過您的 linux 主機時,linux 連接跟踪器 ( conntrack) 分析它們並將有關數據包流的資訊儲存到表 ( conntrack table) 中。每個數據包流都顯示為 conntrack 條目。conntrack 使用元組來辨識數據包流。元組由流的L3(兩端的IP地址和L4協議的編號)和L4(對於TCP這是兩端的埠號資訊)資訊組成。

conntrack 為每個 L4 協議都有一些模組來跟踪傳輸協議特定的連接狀態。TCP conntrack 部分實現了 TCP 有限狀態機。

在實驗室(核心 4.14)中,這個 TCP conntrack 部分有一個奇怪的行為。讓我們在簡單的環境中展示這一點。

客戶端 ( 192.0.2.2) 連接到 linux 主機 ( 192.0.2.1:22),該主機將此連接轉發到其他主機 ( 192.0.2.6:22)。linux 主機也使用 SNAT 規則,就像在您的設置中一樣。

tcpdump 輸出:

14:47:32.036809 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [S], seq 2159011818, win 29200, length 0
14:47:32.037346 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [S.], seq 960236935, ack 2159011819, win 28960, options [mss 1460,sackOK,TS val 1415987649 ecr 3003498128,nop,wscale 5], length 0
14:47:32.037683 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 1, win 913, options [nop,nop,TS val 3003498129 ecr 1415987649], length 0
14:47:32.041407 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [P.], seq 1:22, ack 1, win 905, options [nop,nop,TS val 1415987653 ecr 3003498129], length 21
14:47:32.041806 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 22, win 913, options [nop,nop,TS val 3003498133 ecr 1415987653], length 0
14:47:35.826919 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [F.], seq 1, ack 22, win 913, options [nop,nop,TS val 3003501918 ecr 1415987653], length 0
14:47:35.827996 IP 192.0.2.1.22 > 192.0.2.2.40079: Flags [F.], seq 22, ack 2, win 905, options [nop,nop,TS val 1415991440 ecr 3003501918], length 0
14:47:35.828386 IP 192.0.2.2.40079 > 192.0.2.1.22: Flags [.], ack 23, win 913, options [nop,nop,TS val 3003501919 ecr 1415991440], length 0

在 linux 主機上的 conntrack 中,我看到了這個:

ipv4     2 tcp      6 431999 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431998 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431997 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 431996 ESTABLISHED src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
ipv4     2 tcp      6 119 TIME_WAIT src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2
...
ipv4     2 tcp      6 0 TIME_WAIT src=192.0.2.2 dst=192.0.2.1 sport=40079 dport=22 src=192.0.2.6 dst=192.0.2.5 sport=22 dport=22 [ASSURED] mark=0 zone=0 use=2

如您所見,儘管連接已正確關閉,但關聯的 conntrack 條目仍以TIME_WAIT狀態顯示在 conntrack 表中。而且,因為我們只有一個可能的 SNAT 埠,它已經很忙,新的連接嘗試失敗。為什麼我們不再使用這個埠?因為系統無法區分192.0.2.6目前TIME_WAIT狀態流和新流之間的回複數據包。

為什麼 conntrack 將連接設置為TIME_WAIT狀態而不是破壞它我沒有發現。

引用自:https://serverfault.com/questions/969654