當服務資源為 NLB 時,支援通過 ProxyProtocol v2 協議向後端伺服器傳遞服務使用方的真實來源資訊,包括用戶端真實 IP 和阿里雲擴充資訊(終端節點 ID、VPC ID和終端節點服務 ID),可用於追溯請求來源。
工作原理
擷取用戶端真實 IP
ProxyProtocol v2 用於在 NLB 與後端伺服器之間透傳用戶端串連資訊。配置 NLB 監聽開啟 ProxyProtocol 功能後,NLB 在轉寄用戶端請求時,會在 TCP 三向交握成功後,將用戶端的真實源資訊(如 IP 位址、連接埠等)附加到發送給後端伺服器的第一個 TCP 資料包的 Payload 開頭。
擷取阿里雲擴充資訊
NLB 支援通過 ProxyProtocol v2 的 TLV(Type-Length-Value)擴充機制傳遞 PrivateLink 特有的標識資訊。
根據 ProxyProtocol v2 協議規範,Type 值
0xE0~0xEF為使用者自訂擴充的保留範圍。阿里雲使用0xE1作為自訂 Type 標識。阿里雲擴充資訊格式:
Type:
0xE1,標識阿里雲自訂擴充。Length:後續資料的總長度(包含 SubType + Custom Data)。
SubType:擴充欄位類型。
0x01:VPC ID。0x02:終端節點 ID。0x03:終端節點服務 ID。
Custom Data:擴充資訊的具體 ID。
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *| Type = 0xE1 | Length | SubType | *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ *| | *| | *| Custom Data | *| | *| | *+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
適用範圍
僅 NLB 類型的服務資源支援 ProxyProtocol v2 功能,ALB 和 CLB 不支援。
Proxy Protocol 需要後端伺服器支援該協議才能正常使用。如果後端伺服器不具備解析Proxy Protocol協議能力,直接開啟 ProxyProtocol 功能,很可能會導致後端服務解析異常,從而影響服務可用性。
NLB 監聽支援通過 Proxy Protocol 攜帶原始串連資訊(源IP、目的IP、源連接埠、目的連接埠等)並添加到 TCP 或 UDP 資料頭中,且不會丟棄或覆蓋任何原有資料。
NLB 僅支援Proxy Protocol v2版本。Proxy Protocol v2版本支援多種傳輸協議,如TCP和UDP,更多資訊,請參見The PROXY protocol。
擷取用戶端真實 IP
本文僅介紹服務提供者如何擷取用戶端真實 IP,服務私網訪問的完整流程可參考共用使用者自建服務。
配置 NLB 監聽,開啟 ProxyProtocol:前往NLB 控制台 - 執行個體頁面,單擊目標 NLB 執行個體 ID。在執行個體屬性地區,確保配置修改保護未開啟。
未配置監聽:選擇監聽頁簽,單擊建立監聽,修改高級配置,啟用開啟ProxyProtocol開關。
已配置監聽:單擊監聽 ID,編輯監聽,修改高級配置,啟用開啟ProxyProtocol開關。
配置後端應用解析協議:
此處以CentOS 7.9作業系統、Nginx 1.20.1 版本配置為例。具體請以您實際使用的環境為準。
登入後端伺服器,執行
nginx -t命令查看設定檔所在路徑。預設通常為/etc/nginx/nginx.conf。修改 Nginx 設定檔,在
listen指令中添加proxy_protocol參數。http { # 配置日誌格式,使用$proxy_protocol_addr變數記錄用戶端真實IP log_format main '$proxy_protocol_addr - $remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; server { # 在監聽連接埠上添加proxy_protocol參數 listen 80 proxy_protocol; #... } }執行
sudo nginx -s reload命令,重新載入Nginx設定檔。
驗證後端伺服器可擷取用戶端真實IP:
服務使用方登入終端節點所屬 VPC 的 ECS 執行個體,執行
curl http://<終端節點網域名稱>。服務提供者登入後端伺服器執行
tail -f /var/log/nginx/access.log查看 Nginx 訪問日誌:Nginx記錄檔預設路徑為:
/var/log/nginx/access.log。每行日誌中,
$proxy_protocol_addr變數對應的IP地址即為用戶端真實IP地址。
擷取阿里雲擴充資訊
本文僅介紹服務提供者如何擷取阿里雲擴充資訊,服務私網訪問的完整流程可參考共用使用者自建服務。
配置 NLB 監聽,開啟 ProxyProtocol:前往NLB 控制台 - 執行個體頁面,單擊目標 NLB 執行個體 ID。在執行個體屬性地區,確保配置修改保護未開啟。
未配置監聽:選擇監聽頁簽,單擊建立監聽,修改高級配置,啟用開啟ProxyProtocol開關。
已配置監聽:單擊監聽 ID,編輯監聽,修改高級配置,啟用開啟ProxyProtocol開關。
調用UpdateListenerAttribute,配置
ProxyProtocolEnabled、Ppv2VpcIdEnabled、Ppv2PrivateLinkEpIdEnabled、Ppv2PrivateLinkEpsIdEnabled均為true,通過 Proxy Protocol 協議攜帶 PrivateLink 相關資訊到後端伺服器。配置後端應用解析擴充欄位:
標準應用程式無法直接解析阿里雲擴充欄位。本文以
scapy抓包庫為例,解析並輸出阿里雲擴充資訊。服務提供者登入後端伺服器,執行
pip3 install scapy安裝 scapy。儲存以下指令碼為
parse_ppv2_extensions.py:#!/usr/bin/env python3 from scapy.all import sniff import argparse import sys # 禁用輸出緩衝 sys.stdout = open(sys.stdout.fileno(), mode='w', buffering=1) sys.stderr = open(sys.stderr.fileno(), mode='w', buffering=1) ppv2_signature = [13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10] # "\r\n\r\n\0\r\nQUIT\n" ep_id = "null" vpc_id = "null" eps_id = "null" client_ip = "" client_port = 0 endpoint_ip = "" endpoint_id = "" parser = argparse.ArgumentParser(description="input parameter") parser.add_argument('--dstport', default='destination port', required=True) args = parser.parse_args() rs_port = args.dstport ppv2_cnt = 0 def convert_list_to_string(l): tmp = "" for c in l: tmp += chr(c) return tmp def convert_list_to_ip(l): return ".".join(map(str, l)) def pack_callback(packet): global endpoint_id, client_port, client_ip, vpc_id, ppv2_cnt, endpoint_ip, eps_id if ('Raw' in packet): byte_list = list(packet['Raw'].load) if (byte_list[0:12] == ppv2_signature): ppv2_cnt += 1 client_ip = convert_list_to_ip(byte_list[16:20]) endpoint_ip = convert_list_to_ip(byte_list[20:24]) client_port = byte_list[24] * 256 + byte_list[25] tlv = byte_list[28:] i = 0 while i < len(tlv): tlv_type = tlv[i] tlv_length = tlv[i + 1] * 256 + tlv[i + 2] tlv_value_first_byte = tlv[i + 3] tlv_value_true_value = tlv[i + 4: i + 4 + tlv_length - 1] if tlv_type == 225: # 0xE1 if tlv_value_first_byte == 1: # vpc_id vpc_id = convert_list_to_string(tlv_value_true_value) if tlv_value_first_byte == 2: # endpoint_id endpoint_id = convert_list_to_string(tlv_value_true_value) if tlv_value_first_byte == 3: # eps_id eps_id = convert_list_to_string(tlv_value_true_value) i += 1 + 2 + tlv_length print("receive total %d ppv2 packet, ClientIp: %s, ClientPort: %d, EndpointIp: %s, EndpointId: %s, VpcId: %s, EndpointServiceId: %s" % ( ppv2_cnt, client_ip, client_port, endpoint_ip, endpoint_id, vpc_id, eps_id)) sys.stdout.flush() # 強制重新整理輸出 filterstr = "ip and tcp and dst port " + rs_port print("start to capture ppv2 packet on port " + rs_port) sys.stdout.flush() sniff(iface="eth0", filter=filterstr, prn=pack_callback)執行
sudo python3 parse_ppv2_extensions.py --dstport < NLB伺服器組的後端服務連接埠>。服務使用方登入終端節點所屬 VPC 的 ECS 執行個體,執行
curl http://<終端節點網域名稱>訪問服務後,可在後端伺服器查看到阿里雲擴充資訊。

