全部產品
Search
文件中心

PrivateLink:擷取用戶端真實資訊

更新時間:Jan 23, 2026

當服務資源為 NLB 時,支援通過 ProxyProtocol v2 協議向後端伺服器傳遞服務使用方的真實來源資訊,包括用戶端真實 IP 和阿里雲擴充資訊(終端節點 ID、VPC ID和終端節點服務 ID),可用於追溯請求來源。

工作原理

擷取用戶端真實 IP

ProxyProtocol v2 用於在 NLB 與後端伺服器之間透傳用戶端串連資訊。配置 NLB 監聽開啟 ProxyProtocol 功能後,NLB 在轉寄用戶端請求時,會在 TCP 三向交握成功後,將用戶端的真實源資訊(如 IP 位址、連接埠等)附加到發送給後端伺服器的第一個 TCP 資料包的 Payload 開頭。

Proxy Protocol v2報文參考

攜帶用戶端IPv4地址的Proxy Protocol v2二進位頭格式

攜帶用戶端IPv6地址的Proxy Protocol v2二進位頭格式

IPv4

IPv6

擷取阿里雲擴充資訊

  • 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,服務私網訪問的完整流程可參考共用使用者自建服務
  1. 配置 NLB 監聽,開啟 ProxyProtocol:前往NLB 控制台 - 執行個體頁面,單擊目標 NLB 執行個體 ID。在執行個體屬性地區,確保配置修改保護未開啟。

    • 未配置監聽:選擇監聽頁簽,單擊建立監聽,修改高級配置,啟用開啟ProxyProtocol開關。

    • 已配置監聽:單擊監聽 ID,編輯監聽,修改高級配置,啟用開啟ProxyProtocol開關。

  2. 配置後端應用解析協議:

    此處以CentOS 7.9作業系統、Nginx 1.20.1 版本配置為例。具體請以您實際使用的環境為準。

    1. 登入後端伺服器,執行nginx -t命令查看設定檔所在路徑。預設通常為 /etc/nginx/nginx.conf

    2. 修改 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;
              #...
          }
      }
    3. 執行sudo nginx -s reload命令,重新載入Nginx設定檔。

  3. 驗證後端伺服器可擷取用戶端真實IP:

    1. 服務使用方登入終端節點所屬 VPC 的 ECS 執行個體,執行curl http://<終端節點網域名稱>

    2. 服務提供者登入後端伺服器執行tail -f /var/log/nginx/access.log 查看 Nginx 訪問日誌:

      Nginx記錄檔預設路徑為:/var/log/nginx/access.log
      每行日誌中,$proxy_protocol_addr變數對應的IP地址即為用戶端真實IP地址。

      image

擷取阿里雲擴充資訊

本文僅介紹服務提供者如何擷取阿里雲擴充資訊,服務私網訪問的完整流程可參考共用使用者自建服務
  1. 配置 NLB 監聽,開啟 ProxyProtocol:前往NLB 控制台 - 執行個體頁面,單擊目標 NLB 執行個體 ID。在執行個體屬性地區,確保配置修改保護未開啟。

    • 未配置監聽:選擇監聽頁簽,單擊建立監聽,修改高級配置,啟用開啟ProxyProtocol開關。

    • 已配置監聽:單擊監聽 ID,編輯監聽,修改高級配置,啟用開啟ProxyProtocol開關。

  2. 調用UpdateListenerAttribute,配置ProxyProtocolEnabledPpv2VpcIdEnabledPpv2PrivateLinkEpIdEnabledPpv2PrivateLinkEpsIdEnabled均為true,通過 Proxy Protocol 協議攜帶 PrivateLink 相關資訊到後端伺服器。

  3. 配置後端應用解析擴充欄位:

    標準應用程式無法直接解析阿里雲擴充欄位。本文以scapy抓包庫為例,解析並輸出阿里雲擴充資訊。

    1. 服務提供者登入後端伺服器,執行pip3 install scapy安裝 scapy。

    2. 儲存以下指令碼為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)
      
    3. 執行sudo python3 parse_ppv2_extensions.py --dstport < NLB伺服器組的後端服務連接埠>

    4. 服務使用方登入終端節點所屬 VPC 的 ECS 執行個體,執行curl http://<終端節點網域名稱>訪問服務後,可在後端伺服器查看到阿里雲擴充資訊。

      image