在擷取流量時,你可能會遇到一種狀況:出向(outbound)流量抓得到,回程流量卻抓不到。原因是上游路由器不知道回到原始來源網路的路徑,於是把回程流量整個丟棄、或轉送到不相關的網路。

網路位址轉譯(network address translation, NAT)可以化解這個問題。NAT 會改寫 IP 與更上層協定(如 TCP)封包中的來源與目的位址資訊。它被大量用來延展有限的 IPv4 位址空間——讓多台裝置藏在單一公有 IP 之後——同時也讓網路組態與安全管理更輕鬆:NAT 開啟後,你可以在單一 NAT IP 之後跑任意多台裝置,對外只需管理那一個公有 IP。

目前常見兩種 NAT,差別在於處理流量時改寫的是哪一個位址:

  • 來源 NAT(Source NAT, SNAT,又稱 masquerading):改寫來源 IP 位址。
  • 目的 NAT(Destination NAT, DNAT):改寫目的 IP 位址。

啟用 SNAT#

當你要讓路由器把多台機器藏在單一 IP 之後時,就用 SNAT。開啟 SNAT 後,流量經由對外介面被路由出去時,封包中的來源 IP 會被改寫成 SNAT 提供的那個單一 IP。

為什麼這在擷取時有用?回想前面提過:流量要能在兩節點間往返,兩端都必須有正確的路由資訊。當你想把流量導向一個你並不掌控的網路時,若回程路由資訊不正確,最糟情況是流量只能單向流動;就算是最好情況,你多半也只抓得到單一方向,另一方向會走別條路徑。

SNAT 正是用來解決這個問題:它把流量的來源位址改成目的節點「路由得回去」的位址——通常就是路由器對外介面的那個 IP。如此一來目的節點就能把流量送回路由器方向。

運作流程:用戶端(10.0.0.1)要送封包給另一網路上的伺服器,先送到設有 SNAT 的路由器。路由器收到時,把來源位址由 10.0.0.1 改成自己的 1.1.1.1,再轉送給伺服器。伺服器以為封包來自路由器,於是回程封包送往 1.1.1.1。路由器依目的位址與埠號判定這屬於某條既有的 NAT 連線,把位址還原回 10.0.0.1,再轉送回原始用戶端——伺服器自始至終都不需要知道用戶端的存在、也不必知道怎麼路由到它的網路。

圖表 4-2:從用戶端到伺服器的 SNAT 範例

在 Linux 設定 SNAT#

雖然 Windows 與 macOS 也能用網際網路連線共用(Internet Connection Sharing)設定 SNAT,但這裡只示範 Linux,因為它最容易說明、網路組態也最有彈性。

設定前的準備:

  • 依前一節所述啟用 IP 路由。
  • ifconfig 找出你要設 SNAT 的對外介面名稱(可能類似 eth0)。
  • 記下該對外介面對應的 IP 位址。

設定 NAT 規則前,先用 iptables -t nat -F 清空既有的 NAT 規則,避免舊規則干擾。

Linux:設定 SNAT 的 iptables 指令

先清空既有 NAT 規則(以 root 執行):

iptables -t nat -F

若對外介面是固定位址,將 INTNAME 換成你的對外介面名稱、INTIP 換成該介面的 IP:

iptables -t nat -A POSTROUTING -o INTNAME -j SNAT --to INTIP

若 IP 是動態配置(例如 DHCP 或撥接),改用 MASQUERADE 自動判定對外 IP:

iptables -t nat -A POSTROUTING -o INTNAME -j MASQUERADE

啟用 DNAT#

當你想把流量重導向到一個代理(proxy)或其他服務——在轉送到原始目的地之前先攔截、終結它——DNAT 就派上用場。DNAT 改寫目的 IP 位址,也可選擇性地改寫目的埠。

典型應用是中間人分析(man-in-the-middle):用戶端把目的地為 domain.com:1234 的流量送過路由器,路由器本會直接轉送到原目的地;但因為設了 DNAT,把目的位址與埠改成 192.168.0.10:8888,路由器便依轉送規則把封包送到能擷取流量的代理機器。代理再對伺服器建立一條新連線、轉送用戶端送來的封包。如此一來,原始用戶端與伺服器之間的所有流量都能被擷取與竄改。

圖表 4-3:將流量 DNAT 到代理的範例

DNAT 的設定因路由器所跑的作業系統而異。若你的路由器跑 Windows,大概無能為力,因為所需功能並未開放給使用者。各版類 Unix 系統與 macOS 之間差異也很大,故此處只示範 Linux。

在 Linux 上 DNAT 發生在正常路由規則之前,因此拿本地網路位址當新目的地是安全的;DNAT 規則不會影響 Linux 本機直接送出的流量。

Linux:設定 DNAT 的 iptables 指令

先清空既有 NAT 規則:

iptables -t nat -F

把送往 ORIGIP 的所有封包重導向到 NEWIP(以 root 執行):

iptables -t nat -A PREROUTING -d ORIGIP -j DNAT --to-destination NEWIP

若只想套用到特定 TCP 或 UDP 流量,PROTOtcpudp;也可設定 ORIGPORTNEWPORT 來改寫目的埠(未指定 NEWPORT 時只改 IP):

iptables -t nat -A PREROUTING -p PROTO -d ORIGIP --dport ORIGPORT -j DNAT \
    --to-destination NEWIP:NEWPORT