前言

在家庭实验室(HomeLab)中,我们常使用 FRP 将内网的 1Panel 面板或 Web 服务暴露到公网。为了让内网的 Nginx(OpenResty)自动管理 SSL 证书,很多用户会选择 FRP 的 TCP 模式 直接透传 80 和 443 端口。

但在这种模式下,FRP 充当的是“四层 TCP 转发”角色,它不理解 HTTP 协议,因此无法插入 X-Forwarded-For 请求头。这就导致了一个痛点:Web 日志和应用程序(如 Chevereto)后台看到的来源 IP 全是内网 IP(如 127.0.0.1)。

这会导致图床的 防暴破功能失效,且仪表盘“正在连接的IP无法获取真实IP”。

本文将介绍如何利用 PROXY Protocol,在 FRP TCP 模式 下打通全链路 IP 传递。

重要前提:本教程仅适用于 FRP 的 type = "tcp" 模式(即直接转发 TCP 流量)。如果你使用的是 type = "http"type = "https" 模式,FRP 通常会自动处理 Header,不适用本文的配置方法。

场景架构

  • 公网服务器frps(服务端)。
  • 内网服务器frpc(客户端)、1Panel 面板。
  • 应用环境:1Panel 反向代理 -> Docker 容器 (Chevereto)。
  • FRP 配置模式TCP 模式 (接管 80/443 端口,由内网 Nginx 处理 SSL)。

核心原理

在 TCP 透传模式下,数据包是原样转发的。要获取真实 IP,必须启用 PROXY Protocol v2

  1. FRP 层:开启 proxy_protocol_version = "v2",在 TCP 握手阶段封装真实 IP。
  2. Nginx 层:配置 listen ... proxy_protocol 进行解包。
  3. 应用层:通过环境变量强制 Docker 容器信任解包后的 IP。

第一步:配置内网 FRP 客户端 (frpc)

编辑内网的 frpc 配置文件(frpc.tomlfrpc.ini)。务必确保你的代理类型是 tcp,并添加协议版本配置。

frpc.toml 示例:

[[proxies]]
name = "1panel_80"
type = "tcp"           # <--- 必须是 tcp 模式
localIP = "192.168.5.200"
localPort = 80
remotePort = 80
transport.proxyProtocolVersion = "v2"  # <--- 核心:开启 v2 协议封装 IP

[[proxies]]
name = "1panel_443"
type = "tcp"           # <--- 必须是 tcp 模式
localIP = "192.168.5.200"
localPort = 443
remotePort = 443
transport.proxyProtocolVersion = "v2"  # <--- 核心:开启 v2 协议封装 IP
操作提示:修改配置后需重启 frpc。重启后,在完成下一步 Nginx 配置前,网站将暂时无法访问(因为 Nginx 还没准备好接收 Proxy 协议数据),这是正常的。

第二步:配置 1Panel 网站 Nginx

我们需要手动修改 Nginx 配置文件来“解包”。
注意:请在“配置文件”选项卡中手动修改,不要依赖 1Panel 图形化界面中的“真实 IP”设置,以免配置被覆盖。

  1. 在 1Panel 面板中,进入 网站 -> 你的域名 -> 配置 -> 配置文件
  2. 找到 server 块,修改 listen 指令并添加 IP 识别逻辑:
server {
    # 1. 核心修改:在端口后追加 proxy_protocol
    # 只有加了这个,Nginx 才能看懂 FRP 发过来的特殊数据包
    listen 80 proxy_protocol; 
    listen 443 ssl proxy_protocol; 
    
    server_name img.yourdomain.com; 
    
    # ... SSL 等常规配置保持不变 ...

    # ========================================================
    # 2. 新增 RealIP 模块配置
    # ========================================================
    
    # 告诉 Nginx:真实 IP 藏在 PROXY 协议头里,而不是 HTTP Header 里
    real_ip_header proxy_protocol; 
    
    # 信任发送 PROXY 包的 IP (即你内网 frpc 所在的 IP)
    # 同时也建议信任本机回环
    set_real_ip_from 192.168.5.200; 
    set_real_ip_from 127.0.0.1;
    
    # ========================================================
    
    include /www/sites/img.yourdomain.com/proxy/*.conf; 
}

修改完成后,点击 “保存并重载”
此时查看 Nginx 日志,应该能看到真实的公网 IP 了。


第三步:配置 Chevereto 容器 (应用层适配)

虽然 Nginx 拿到了 IP,但 Docker 容器内的 Chevereto 默认只读取直连它的网关 IP(如 172.17.0.1)。我们需要“隔山打牛”,通过环境变量传递 IP。

  1. 在 1Panel 点击 “容器”,找到 Chevereto 容器,点击 “编辑”
  2. “环境” 选项卡中,新增一行
键 (Key)值 (Value)
CHEVERETO_HEADER_CLIENT_IPX-Real-IP
  1. 点击 “确认” 重建容器。
原理:Nginx 获取到真实 IP 后,默认会将其放入 X-Real-IP 请求头传给后端。这个环境变量是告诉 Chevereto:“别看连接 IP 了,直接读 X-Real-IP 头里的数据”。

验证结果

  1. Nginx 日志:访问日志 (access.log) 中,来源 IP 已变为公网 IP。
  2. Chevereto 仪表盘

    • 进入仪表盘,查看 “正在连接的IP”
    • 数值应显示为你当前的公网 IP。

总结

使用 FRP TCP 模式 穿透时,PROXY Protocol 是传递真实 IP 的唯一标准解法。通过本文的“FRP 封包 -> Nginx 解包 -> 环境变量透传”三步走策略,我们成功在复杂的内网 Docker 环境中还原了用户真实身份。

最后修改:2025 年 12 月 19 日
如果觉得我的文章对你有用,请随意赞赏