Files
flvx/docs/proxy-protocol-analysis.md

5.3 KiB

Proxy Protocol 传输分析报告

日期: 2026-05-07
测试环境: 20.118.172.127 (Server 1) ↔ 108.181.90.137 (Server 2)


1. 代码流程分析

完整数据链路

前端 (proxyProtocol: 0|1|2)
  → 后端 handler mutations.go:1936
  → 数据库存储 forward.proxy_protocol (model.go:50)
  → 控制面 buildForwardServiceConfigs (control_plane.go:1791-1796)
  → handler metadata: {"proxyProtocol": 2}
  → Agent metadata 解析 (metadata.go:42)
  → handler.go:256 WrapClientConn()
  → conn.go:14 HeaderProxyFromAddrs(byte(ppv), src, dst)
  → conn.go:15 header.WriteTo(c)
  → 目标服务器收到 PROXY protocol header

关键代码

写入 PROXY header (go-gost/x/internal/net/proxyproto/conn.go):

func WrapClientConn(ppv int, src, dst net.Addr, c net.Conn) net.Conn {
    if ppv <= 0 {
        return c
    }
    header := proxyproto.HeaderProxyFromrs(byte(ppv), src, dst)
    header.WriteTo(c)
    return c
}

Handler 调用 (go-gost/x/handler/forward/local/handler.go:256):

cc = proxyproto.WrapClientConn(h.md.proxyProtocol, conn.RemoteAddr(), conn.LocalAddr(), cc)
  • src = conn.RemoteAddr() → 客户端真实 IP ✅
  • dst = conn.LocalAddr() → agent 监听地址 ✅
  • ppv = 1 或 2 → 版本号正确 ✅

2. 实际传输测试结果

测试方法

  1. 在 Server 2 启动 Python TCP 监听器,解析 PROXY protocol header
  2. 在 Server 1 用当前代码编译 gost,配置 proxyProtocol: 2 转发到 Server 2
  3. 通过 nc 发送测试数据,验证 Server 2 是否收到正确的 PROXY header

测试结果

版本 状态 接收到的 Header
PPv2 ✅ 成功 PP2 family=1 alen=12 SRC=127.0.0.1:45410 DST=127.0.0.1:20001
PPv1 ✅ 成功 PROXY TCP4 127.0.0.1 127.0.0.1 43816 20001

测试详情

PPv2 原始数据:

Got 28 bytes
PP2 family=1 alen=12
SRC=127.0.0.1:45410 DST=127.0.0.1:20001

PPv1 原始数据 (hex):

50524f58592054435034203132372e302e302e31203132372e302e302e312034333831362032303030310d0a

解码: PROXY TCP4 127.0.0.1 127.0.0.1 43816 20001


3. 单元测试结果

go-gost/x/handler/forward/local/  → TestLocalForwardHandlerSendsProxyProtocolToTarget ✅
go-backend/internal/http/handler/ → TestBuildForwardServiceConfigsSendsProxyProtocolToForwardHandler ✅
go-backend/internal/store/repo/   → TestGetForwardRecordIncludesProxyProtocol ✅

全部通过 (3/3)。


4. 发现的问题

问题 1: WriteTo 错误未检查

位置: go-gost/x/internal/net/proxyproto/conn.go:15

header.WriteTo(c)  // 返回 (int64, error) 被忽略

影响: 如果写入失败(连接已断开、网络错误等),后续数据传输会在没有 PROXY header 的情况下继续,目标服务器可能解析出错。

建议:

if _, err := header.WriteTo(c); err != nil {
    return c  // 或包装一个带错误的 conn
}

严重程度: 低(实际场景中,写入失败后 Transport 也会很快失败)


问题 2: 部署版本过旧

服务器状态:

服务器 组件 版本 状态
20.118.172.127 flux_agent UPX 压缩,无法读取版本 ✅ 运行中
20.118.172.127 paneld /app/paneld ✅ 运行中
20.118.172.127 /usr/local/bin/gost v3.0.0 (go1.23.4) 旧版,不支持 handler metadata 中的 proxyProtocol
108.181.90.137 flux_agent 8.8MB ✅ 运行中

影响: 旧版 gost 二进制不识别 handler metadata 中的 proxyProtocol 字段,PROXY protocol 功能在生产环境不可用。

验证: 用旧版 gost 测试时,目标服务器收到的原始数据为空,无 PROXY header。


问题 3: 数据库 Schema 缺失

位置: 20.118.172.127 的 /app/data/gost.db

当前 forward 表 schema:

CREATE TABLE `forward` (
    `id` integer PRIMARY KEY AUTOINCREMENT,
    `user_id` integer NOT NULL,
    `user_name` varchar(100) NOT NULL,
    `name` varchar(100) NOT NULL,
    `tunnel_id` integer NOT NULL,
    `remote_addr` text NOT NULL,
    `strategy` varchar(100) NOT NULL DEFAULT "fifo",
    `in_flow` integer NOT NULL DEFAULT 0,
    `out_flow` integer NOT NULL DEFAULT 0,
    `created_time` integer NOT NULL,
    `updated_time` integer NOT NULL,
    `status` integer NOT NULL,
    `inx` integer NOT NULL DEFAULT 0,
    `speed_id` integer
);

缺失字段:

  • proxy_protocol — PROXY protocol 版本
  • max_conn — 最大连接数
  • ip_max_conn — 每 IP 最大连接数
  • ip_speed_id — 每 IP 限速 ID

影响: 后端无法存储和读取 proxy_protocol 配置,前端设置不会生效。


5. 结论

维度 状态 说明
代码实现 ✅ 正确 完整的写入链路,版本/地址正确
单元测试 ✅ 通过 3/3 测试覆盖 handler、repo、控制面
实际传输 (新编译版) ✅ 成功 PPv1 和 PPv2 均正确传输
实际传输 (部署版) ❌ 不工作 旧版不支持 handler metadata 中的 proxyProtocol
数据库 Schema ❌ 缺字段 需要迁移添加 proxy_protocol 等列

总结: 代码实现正确,PROXY protocol 传输逻辑无误。但生产服务器运行的是旧版本,需要升级 backend 和 agent 才能启用此功能。