Skip to content

feat: support proxy protocol#422

Merged
driftluo merged 1 commit intomasterfrom
support-proxy-protocol
Feb 6, 2026
Merged

feat: support proxy protocol#422
driftluo merged 1 commit intomasterfrom
support-proxy-protocol

Conversation

@driftluo
Copy link
Collaborator

@driftluo driftluo commented Feb 5, 2026

Feature

  1. Support parsing haproxy protocol on TCP
  2. Support parsing X-Forwarded-For/X-Forwarded-Port on WebSocket

Both can help Tentacle in resolving the actual origin address after a proxy.

Test

Nginx

Nginx only supports proxy protocol v1

nginx conf:

worker_processes 1;
error_log /dev/stderr info;
pid /tmp/nginx-proxy-test.pid;

events {
    worker_connections 1024;
}

stream {
    log_format proxy '$remote_addr [$time_local] '
                     '$protocol $status $bytes_sent $bytes_received '
                     '$session_time "$upstream_addr"';
    
    access_log /dev/stdout proxy;

    # TCP proxy - send PROXY Protocol v1
    # client connect 127.0.0.1:8000 -> nginx -> 127.0.0.1:9000 (backend)
    upstream tcp_backend {
        server 127.0.0.1:9000;
    }

    server {
        listen 8000;
        
        # enable proxy protocol v1
        proxy_pass tcp_backend;
        proxy_protocol on;
        
        proxy_connect_timeout 10s;
        proxy_timeout 300s;
    }
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /dev/stdout main;

    sendfile on;
    keepalive_timeout 65;

    # WebSocket backend
    upstream ws_backend {
        server 127.0.0.1:9001;
    }

    # client connect ws://127.0.0.1:8080 -> nginx -> 127.0.0.1:9001 (backend)
    server {
        listen 8080;
        server_name localhost;
        
        access_log /dev/stdout main;

        location / {
            proxy_pass http://ws_backend;
            
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Port $remote_port;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            proxy_connect_timeout 10s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
        }
    }
}

Run Docker with:

$ docker run --rm -d --name nginx-proxy-test \
     --network host \
     -v $(pwd)/nginx-proxy-test.conf:/etc/nginx/nginx.conf:ro \
     nginx:alpine

Haproxy

haproxy conf

global
    log stdout format raw local0 info
    maxconn 4096

defaults
    log     global
    mode    tcp
    option  tcplog
    option  dontlognull
    timeout connect 10s
    timeout client  300s
    timeout server  300s

# ============================================
# TCP prxoy - PROXY Protocol v1
# ============================================
frontend tcp_proxy_v1
    bind *:8000
    default_backend tcp_backend_v1

backend tcp_backend_v1
    server tentacle 127.0.0.1:9000 send-proxy

# ============================================
# TCP prxoy - PROXY Protocol v2
# ============================================
frontend tcp_proxy_v2
    bind *:8001
    default_backend tcp_backend_v2

backend tcp_backend_v2
    # send-proxy-v2 enable PROXY Protocol v2
    server tentacle 127.0.0.1:9000 send-proxy-v2

# ============================================
# WebSocket prxoy - X-Forwarded-For
# ============================================
frontend ws_proxy
    bind *:8080
    mode http
    default_backend ws_backend

backend ws_backend
    mode http
    # add X-Forwarded-For and X-Forwarded-Port on header
    option forwardfor
    http-request set-header X-Forwarded-Port %[src_port]
    server tentacle 127.0.0.1:9001

Run Docker with:

$ docker run --rm -d --name haproxy-proxy-test --network host \
  -v $(pwd)/haproxy-proxy-test.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  haproxy:alpine

tentacle

Write two Tentacle programs:

  1. A server, listening on port 9000 (TCP), and the websocket listening on port 9001.
  2. A client, using a reused port to bind TCP to port 12345 and the WS to port 12346. Connect the TCP connection to port 9000 and the WS to port 9001. Check the port information on the server; it should match the source address of the reused port.

@driftluo driftluo requested a review from eval-exec as a code owner February 5, 2026 08:36
@driftluo driftluo force-pushed the support-proxy-protocol branch 4 times, most recently from 2c62f65 to 31694e1 Compare February 5, 2026 11:32
@quake quake requested a review from Copilot February 5, 2026 12:12
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for HAProxy PROXY protocol (v1 and v2) and X-Forwarded-For header parsing to help Tentacle resolve the actual origin address when connections come through a proxy.

Changes:

  • Added comprehensive PROXY protocol v1/v2 parser module with full test coverage
  • Integrated proxy protocol parsing into TCP connection handling
  • Added X-Forwarded-For and X-Forwarded-Port header extraction for WebSocket connections
  • Introduced trusted_proxies configuration (defaults to loopback addresses) to control when proxy headers are parsed
  • Updated Rust toolchain to 1.92.0 and modernized code using div_ceil

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tentacle/src/transports/proxy_protocol.rs New module implementing HAProxy PROXY protocol v1/v2 parsing with comprehensive unit tests
tentacle/src/transports/tcp_base_listen.rs Integrated proxy protocol and X-Forwarded-For parsing into connection handling based on trusted proxy list
tentacle/src/transports/tcp.rs Added trusted_proxies field to TcpTransport
tentacle/src/transports/mod.rs Added proxy_protocol module export
tentacle/src/service/config.rs Added trusted_proxies configuration with default localhost values
tentacle/src/service.rs Passes trusted_proxies through to MultiTransport; minor clippy fixes
tentacle/src/builder.rs Added trusted_proxies() builder method with documentation
tentacle/tests/test_proxy_protocol.rs Comprehensive integration tests covering TCP PROXY protocol and WebSocket X-Forwarded-For scenarios
tentacle/src/channel/unbound.rs Added clippy::unnecessary_unwrap allowance for complex drop logic
tentacle/src/channel/bound.rs Added clippy::unnecessary_unwrap allowance for complex drop logic
secio/src/dh_compat/openssl_impl.rs Modernized ceiling division using div_ceil method
rust-toolchain Updated Rust version from 1.85.0 to 1.92.0

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@driftluo driftluo force-pushed the support-proxy-protocol branch 4 times, most recently from 4779b85 to 1d76120 Compare February 6, 2026 02:29
@driftluo driftluo force-pushed the support-proxy-protocol branch from 1d76120 to ea67814 Compare February 6, 2026 05:47
@driftluo driftluo merged commit 169bed4 into master Feb 6, 2026
7 checks passed
@driftluo driftluo deleted the support-proxy-protocol branch February 6, 2026 12:52
sunchengzhu added a commit to sunchengzhu/ckb-proxy-protocol-test that referenced this pull request Feb 10, 2026
根因: 两个因素叠加导致 WS 节点重启后永久无法重连

1. peer_store 的 base_addr() 会剥离 /ws 后缀
   存入: /ip4/x.x.x.x/tcp/8231/ws/p2p/...
   取出: /ip4/x.x.x.x/tcp/8231/p2p/...(丢失 /ws)
   重连时 find_type() 判定为 TCP 传输
   用原始 TCP 连接 HAProxy HTTP 端口 -> 永久失败

2. HAProxy HTTP 模式断开传播较慢
   TCP 模式: 收到 FIN -> 立即关闭后端 -> Node B 清理 session
   HTTP 模式: WebSocket 隧道清理有延迟 -> 旧 session 残留
   新连接到达时 PeerIdExists -> 拒绝

TCP PP 节点不受影响:
   base_addr() 不剥离 TCP 地址的任何部分
   HAProxy TCP 模式断开传播即时

修复:
- 启动前清理 WS 节点的 peer_store 强制使用带 /ws 的 bootnode
- 跨机场景增加 3 秒延迟 等待服务端旧 session 清理

参考: nervosnetwork/ckb#5105, nervosnetwork/tentacle#422
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants