Nftables

如何使用 nftables 將埠 80 上的請求重定向到 localhost:3000?

  • June 20, 2020

我希望將到達 192.168.0.1:80 的網路流量重定向到 127.0.0.1:3000。而且,我希望也能處理響應的映射。我完整的 NAT 和過濾表規則粘貼在下面。

我能夠在埠 80 上接收連接。但是,我無法將流量重定向到 localhost:3000。

add table inet filter
add chain inet filter input { type filter hook input priority 0; policy accept; }
add chain inet filter forward { type filter hook forward priority 0; policy accept; }
add chain inet filter output { type filter hook output priority 0; policy accept; }
add rule inet filter input ct state related,established  counter accept
add rule inet filter input ip protocol icmp counter accept
add rule inet filter input iifname "lo" counter accept
add rule inet filter input ct state new  tcp dport 80 counter accept
add rule inet filter input ct state new  tcp dport 4489 counter accept
add rule inet filter input ct state new  tcp dport 8080 counter accept
add rule inet filter input iifname "tun0" ct state new  tcp dport 139 counter accept
add rule inet filter input iifname "tun0" ct state new  tcp dport 445 counter accept
add rule inet filter input ct state new  udp dport 1194 counter accept
add rule inet filter input counter reject with icmp type host-prohibited
add rule inet filter forward counter reject with icmp type host-prohibited
add table nat
add chain nat prerouting { type nat hook prerouting priority -100; }
add chain nat postrouting { type nat hook postrouting priority 100; }
add rule nat prerouting redirect
add rule nat prerouting tcp dport 80 redirect to 3000
add rule nat prerouting iifname eth0 tcp dport { 80, 443 } dnat 127.0.0.1:3000
add rule nat postrouting oifname eth0 snat to 192.168.0.1

我將嘗試解決並完成OP 自己的工作答案和進一步的評論,其中包括一些剩餘的問題:

  • 為什麼net.ipv4.conf.eth0.route_localnet=1需要?
  • 為什麼需要在eth0而不是lo上允許埠 3000 ?

並且還將解決一個小的安全問題。

首先這裡是關於 Netfilter 和通用網路中的數據包流的強制性示意圖:

Netfilter 和通用網路中的數據包流

這個示意圖是在考慮iptables的情況下製作的,但nftables可以(並且在大多數預設規則集中)在相同的地方使用相同的鉤子。

當數據包到達網路層(IP 第 3 層)時,它由各種子系統處理。通常只有路由堆棧,但這裡Netfilter為自己(conntrack,包括初始數據包後的 NAT 處理)或nftables提供掛鉤。

Netfilter ( conntrack ) 或nftables不關心路由(除非例如nftables使用與路由相關的專門表達式,它們將其留給路由堆棧:它們操作地址和埠,nftables然後檢查可用屬性,如介面、地址和港口。

所以:

  • 新連接中的數據包(因此也遍歷ip nat prerouting )從**eth0到達(例如)源地址 192.0.2.2 和埠 45678 目標地址 192.168.0.1 和埠 80(或 443)。
  • ip nat prerouting dnat規則匹配並告訴netfilter(其conntrack子系統)將目標地址更改為 127.0.0.1 並將目標埠更改為 3000。這不會更改數據包的任何其他屬性。特別是數據包仍然從eth0到達。
  • 路由堆棧(示意圖中的路由決策)不依賴於Netfilter,因此在邏輯上保持獨立於它並且不知道先前的更改。它現在必須處理來自 192.0.2.2 和目標 127.0.0.1 的數據包。

這是一個反常現象:如RFC 1122中所述,它將允許“在 Internet”上看到為環回保留的地址範圍:

(G) { 127, <any> }

內部主機環回地址。這種形式的地址不得出現在主機之外。

這在 Linux 核心的路由堆棧中明確處理:將其視為火星目的地(即:丟棄數據包),除非通過在相關介面上使用來放鬆。這就是為什麼必須針對這種特定情況進行設置。route_localnet=1net.ipv4.conf.eth0.route_localnet=1

  • 同樣,下一個nftables規則,這次來自過濾器輸入鉤子,看到一個輸入介面仍然為eth0但目標埠現在為 3000 的數據包。因此它必須允許目標埠 3000,並且不再允許 80(或 443)接受它。所以規則應該縮短為:
iifname "eth0" tcp dport {4489, 3000} counter accept

因為它永遠不會看到來自eth0且目標 tcp 埠為 80 或 443 的數據包:它們在之前的 nat 預路由鉤子中都更改為埠 3000。此外,為了解釋起見,假設看到此類數據包,它們將被接受,但由於埠 80 或 443 上沒有偵聽程序(它正在偵聽埠 3000),tcp 堆棧將發出 TCP 重置以拒絕連接。

此外,雖然路由堆棧強制執行 127.0.0.0/8 和**lo介面之間的某些關係(使用 進一步放寬route_localnet=1),但如前所述,這與netfilternftables 無關,它們不介意路由。此外,如果是這種情況,對於輸入介面,這將是未更改的源地址,而不是與在**輸入路徑中甚至沒有真正含義的輸出介面相關的**目標地址:或不能在這裡使用。僅在過濾器輸入鉤子中的事實oif``oifname已經意味著評估的數據包正在到達主機以進行本地程序,如示意圖所示。

更新:實際上,出於安全原因,應該進一步更改先前給出的規則:埠 3000 被允許,但不僅僅是目標 127.0.0.1。因此,與 192.168.0.1:3000 的連接可以接收 TCP RST,它暗示這裡有一些特別的東西,而不是沒有得到任何回复。為了解決這種情況:

  • 要麼使用這個(其中包括一個非常奇怪的第二條規則):
iifname "eth0" tcp dport 4489 counter accept
iifname "eth0" ip daddr 127.0.0.1 tcp dport 3000 counter accept

`route_localnet=1`仍然允許同一 192.168.0.0/24 LAN 中的經過調整的系統通過線上路上發送帶有 127.0.0.1 的數據包來訪問服務而不使用 NAT,即使這樣做可能沒有任何好處。例如另一個 Linux 系統,使用以下 4 個命令:
sctl -w net.ipv4.conf.eth0.route_localnet=1
 address delete 127.0.0.1/8 dev lo # can't have 127.0.0.1 also local
 route add 127.0.0.1/32 via 192.168.0.1 # via, that way no suspicious ARP *broadcast* for 127.0.0.1 will be seen elsewhere.
cat tcp4:127.0.0.1:3000 -
或者相反,也為上述情況提供保護,更通用且更受歡迎:
fname "eth0" tcp dport 4489 counter accept
 status dnat counter accept

它像以前一樣允許不相關的埠 4489/tcp
`ct status dnat` [如果數據包先前由主機 DNATed,則匹配](https://manpages.debian.org/nftables/nft.8#CONNTRACK_TYPES):因此它將接受任何先前的更改,而無需明確重申它是哪個埠(仍然可以聲明它或其他任何內容以進一步縮小接受的範圍):現在埠值 3000*也*不必再明確說明。
因此,它也不允許直接連接到埠 3000,因為這種情況不會被 DNATed。
* 只是為了完整:同樣的事情發生在(不完全)輸出和回复的相反順序。*允許最初生成的從 127.0.0.1 到 192.0.2.2 的傳出數據包在輸出路徑的路由決策*`net.ipv4.conf.eth0.route_localnet=1`中不被視為*火星源*(=> 丟棄),然後才有機會“取消 DNA 處理”回到原來的預期源地址(192.168.0.1) 僅由*netfilter* ( *conntrack* )。




---


當然,使用`route_localnet=1`是一種放鬆的安全性(與足夠的防火牆規則並不真正相關,但並非所有系統都使用防火牆)並且需要有關其使用的相關知識(例如:*如果*沒有`route_localnet=1`環境)。


既然在上面的解釋中解決了安全問題(請參閱“更新”),如果允許應用程序偵聽 192.168.0.1(或任何地址)而不是僅 127.0.0.1,則可以在不啟用的情況下進行等效配置`route_localnet=1`, 通過改變`ip nat prerouting`:



> 
> 
> ```
> iif eth0 tcp dport { 80, 443 } counter dnat 127.0.0.1:3000
> 
> ```
> 
> 


到:

iif eth0 tcp dport { 80, 443 } counter dnat to 192.168.0.1:3000


或簡單地:

iif eth0 tcp dport { 80, 443 } counter redirect to :3000


差別不大:`redirect`將目標更改為介面*eth0*上主機的主 IP 地址,即 192.168.0.1,因此大多數情況下的行為相同。

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