A10 Networks Thunder 實戰課程

3. SNAT 與 Client IP 保存

深入解析來源位址轉換 (Source NAT) 的架構必要性與 X-Forwarded-For 標頭應用

一、核心概念 (Concept)

一句話定義

**Source NAT (SNAT)** 是指負載平衡器 (ADC) 在將封包轉發給後端伺服器時,將封包的「來源 IP」修改為 ADC 自己的 IP,以確保伺服器的回程封包能正確回到 ADC,而不是繞過 ADC 直接回給客戶端。

生活化比喻

想像你是公司的**總機 (ADC)**,客戶 (Client) 打電話進來找財務部 (Server)。

如果你轉接電話時**不做 SNAT**,財務部看到的是客戶的原始號碼,講完後財務部可能會**直接回撥**給客戶,但客戶會覺得很奇怪:「我明明是打給總機,怎麼是財務部私人手機打回來?」這通話就斷了 (非對稱路由)。

**做了 SNAT**,就像是你轉接時告訴財務部:「這通電話是我轉過來的,有事請回撥給我 (ADC IP)。」這樣所有溝通都經過你,你能掌控全程。

而 **X-Forwarded-For** 就像是你雖然用內線轉接,但在電話上貼了一張便利貼:「這是王先生打來的,他的號碼是 0912...」,讓財務部知道真正的發話者是誰。

二、運作原理 (Mechanism)

為什麼需要 SNAT? (非對稱路由問題)

在常見的 **單臂模式 (One-Arm Mode)** 部署中,ADC 與 Server 位於同一個網段。若不開啟 SNAT:

  • Request: Client (IP: A) → VIP (IP: B) → Server (IP: C)。Server 收到封包,來源 IP 為 A。
  • Response: Server 發現 A 不在同一網段,查路由表,直接透過 Gateway (Firewall/Router) 回給 A,跳過了 ADC
  • Result: Client 收到來自 IP C 的封包,但他建立連線的對象是 IP B (VIP)。Client 的 OS 會直接丟棄封包 (RST),連線失敗。
SNAT 配置模式
  • Auto SNAT: ADC 自動使用「出站介面」的 IP 作為來源 IP。設定最簡單,適用於小規模。
  • SNAT Pool: 定義一群 IP 位址池供轉換使用。適用於高併發量,避免單一 IP 的 Port (65535個) 耗盡問題。
Client IP 保存 (X-Forwarded-For)
  • 技術: 在 HTTP Header 中插入 X-Forwarded-For: <Client-IP>
  • 前提: 必須是 HTTP/HTTPS 流量。若是 HTTPS,ADC 必須進行 SSL Offload (解密) 才能修改 Header。
  • 設定: 在 A10 的 template http 中啟用 insert-client-ip

三、架構視覺化 (Visuals)

以下圖表展示「無 SNAT 的問題」與「有 SNAT 的正確流向」,以及「X-Forwarded-For」的運作層次。

場景 A: 未開啟 SNAT (連線失敗)
graph TD Client((Client
1.1.1.1)) Router[Router/Gateway] VIP[A10 ADC
VIP: 2.2.2.2] Server[Web Server
10.0.0.10] %% 流量去程 Client -- "1. Request (Dst:2.2.2.2)" --> Router Router --> VIP VIP -- "2. Forward (Src:1.1.1.1)" --> Server %% 流量回程 (問題所在) Server -. "3. Direct Reply (Dst:1.1.1.1)" .-> Router Router -. "4. Bypass ADC" .-> Client %% 樣式設定 - 強制高對比 %% Client/Server/Router: 白底黑字,粗黑框 style Client fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 style Server fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 style Router fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 %% VIP: 深藍底白字,粗黑框 style VIP fill:#001f3f,stroke:#000000,stroke-width:2px,color:#ffffff linkStyle 3,4 stroke:red,stroke-width:3px,stroke-dasharray: 5 5;
場景 B: 開啟 SNAT (連線成功)
graph TD Client((Client
1.1.1.1)) Router[Router/Gateway] VIP[A10 ADC
SNAT IP: 10.0.0.254] Server[Web Server
10.0.0.10] %% 流量去程 Client -- "1. Request" --> Router Router --> VIP VIP -- "2. SNAT (Src:10.0.0.254)" --> Server %% 流量回程 (正確) Server -- "3. Reply (Dst:10.0.0.254)" --> VIP VIP -- "4. Reply (Src:2.2.2.2)" --> Router Router --> Client %% 樣式設定 - 強制高對比 style Client fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 style Server fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 style Router fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000 style VIP fill:#001f3f,stroke:#000000,stroke-width:2px,color:#ffffff linkStyle 2 stroke:#d5006d,stroke-width:4px; linkStyle 3 stroke:#d5006d,stroke-width:4px;
場景 C: X-Forwarded-For 標頭插入
sequenceDiagram participant C as Client (1.1.1.1) participant A as A10 Thunder (SNAT IP: 10.0.0.254) participant S as Web Server C->>A: HTTP GET / (Src: 1.1.1.1) Note over A: 1. 執行 Source NAT (改 IP)
2. 插入 Header: X-Forwarded-For: 1.1.1.1 A->>S: HTTP GET / (Src: 10.0.0.254)
Header {X-Forwarded-For: 1.1.1.1} Note over S: Server Log 紀錄:
來源IP: 10.0.0.254
XFF: 1.1.1.1 S-->>A: HTTP 200 OK A-->>C: HTTP 200 OK

四、實務應用場景 (Use Case)

金融業網銀系統日誌稽核

背景描述:

某銀行的網銀系統部署在 A10 ADC 後方 (One-Arm 架構)。為了確保路由對稱性,網管人員開啟了 SNAT。然而,資安團隊在審查 Web Server (Apache/Nginx) 的 Access Log 時,發現**所有請求的來源 IP 都是 A10 的內網 IP**,導致無法追蹤駭客攻擊的真實來源,也不符合法規稽核要求。

解決方案:
  1. A10 端: 在 SLB 配置中,建立一個 template http,啟用 insert-client-ip 選項,並將此 template 綁定到 Virtual Service (Port 443/80)。
  2. Server 端: 修改 Web Server 設定檔 (如 Nginx 的 log_format),使其不再只記錄 `$remote_addr`,而是優先記錄 `$http_x_forwarded_for`。
優點:
  • 維持路由架構簡單 (One-Arm)。
  • 滿足資安稽核要求,可看見真實客戶 IP。
  • 不需更改網路底層路由 (Default Gateway)。
潛在缺點:
  • 若應用程式不是 HTTP (如 DB 連線),無法插入 Header,需改用 TCP Option (TOA) 或 Proxy Protocol。
  • Server 端需要配合修改 Log 設定。

進階場景:伺服器間的連線 (Server-to-VIP) 與 ACL SNAT

場景描述:

VIP1 (Frontend): 10.1.1.1 Real Servers: 192.168.1.1, 192.168.1.2

VIP2 (Internal): 10.1.1.2 Real Servers: 192.168.1.3, 192.168.1.4

挑戰:Client 連線到 VIP1 後,後端 Server (192.168.1.1) 需要發起連線去呼叫 VIP2。
問題分析:

192.168.1.1 (RS1) 連線到 10.1.1.2 (VIP2) 時,若 VIP2 將流量分派給 192.168.1.3 (RS3),且沒有做 SNAT:
1. RS3 收到封包,來源 IP 是 192.168.1.1。
2. RS3 發現來源 IP (192.168.1.1) 與自己在同一網段 (192.168.1.x/24)。
3. RS3 直接回覆 (Direct Return) 給 192.168.1.1,跳過 ADC。
4. RS1 收到來自 RS3 IP 的回應,但它原本是呼叫 VIP2 IP,因此 RS1 會丟棄封包,連線失敗。

最佳解法:ACL-based SNAT

我們不希望對所有連到 VIP2 的外部流量都做 SNAT (可能需要保留外部真實 IP),我們只希望「當來源是內部網段 (192.168.1.x) 時」才強制 SNAT。

graph TD ExtUser[外部使用者] RS1[RS1: 192.168.1.1] VIP2[VIP2: 10.1.1.2] RS3[RS3: 192.168.1.3] subgraph "VIP1 流程" ExtUser -->|1. 請求| VIP1[VIP1: 10.1.1.1] VIP1 -->|2. 分派| RS1 end subgraph "Server-to-VIP 流程 (需 ACL SNAT)" RS1 -->|3. 呼叫 VIP2| VIP2 VIP2 -->|4. 觸發 ACL SNAT
Src改為 ADC IP| RS3 RS3 -->|5. 回應給 ADC| VIP2 VIP2 -->|6. 回應給 RS1| RS1 end %% Styling for clarity %% ExtUser: 白底,黑字,黑框 style ExtUser fill:#ffffff,stroke:#000000,stroke-width:2px,color:#000000,font-weight:bold %% VIP nodes: 深藍底,白字,黑框 style VIP1 fill:#001f3f,stroke:#000000,stroke-width:2px,color:#ffffff style VIP2 fill:#001f3f,stroke:#000000,stroke-width:2px,color:#ffffff %% Server nodes (RS1, RS3): 淺藍底,黑字 (強制),深藍框 style RS1 fill:#e0e7ff,stroke:#001f3f,stroke-width:2px,color:#000000 style RS3 fill:#e0e7ff,stroke:#001f3f,stroke-width:2px,color:#000000 linkStyle 3 stroke:#d5006d,stroke-width:4px;
設定邏輯 (虛擬碼):
1. 定義 ACL: 
   access-list 100 permit ip 192.168.1.0 0.0.0.255 any (比對來源是 Server 網段)

2. 在 VIP2 的 Virtual Port 綁定 SNAT:
   slb virtual-server VIP2 10.1.1.2
     port 80 tcp
       source-nat pool SNAT_POOL acl 100 (只對符合 ACL 100 的流量做 SNAT)
                        

五、隨堂測驗 (Quiz)