前言
在家庭实验室(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 传递。
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:
- FRP 层:开启
proxy_protocol_version = "v2",在 TCP 握手阶段封装真实 IP。 - Nginx 层:配置
listen ... proxy_protocol进行解包。 - 应用层:通过环境变量强制 Docker 容器信任解包后的 IP。
第一步:配置内网 FRP 客户端 (frpc)
编辑内网的 frpc 配置文件(frpc.toml 或 frpc.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”设置,以免配置被覆盖。
- 在 1Panel 面板中,进入 网站 -> 你的域名 -> 配置 -> 配置文件。
- 找到
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。
- 在 1Panel 点击 “容器”,找到 Chevereto 容器,点击 “编辑”。
- 在 “环境” 选项卡中,新增一行:
| 键 (Key) | 值 (Value) |
|---|---|
| CHEVERETO_HEADER_CLIENT_IP | X-Real-IP |
- 点击 “确认” 重建容器。
原理:Nginx 获取到真实 IP 后,默认会将其放入X-Real-IP请求头传给后端。这个环境变量是告诉 Chevereto:“别看连接 IP 了,直接读X-Real-IP头里的数据”。
验证结果
- Nginx 日志:访问日志 (
access.log) 中,来源 IP 已变为公网 IP。 Chevereto 仪表盘:
- 进入仪表盘,查看 “正在连接的IP”。
- 数值应显示为你当前的公网 IP。
总结
使用 FRP TCP 模式 穿透时,PROXY Protocol 是传递真实 IP 的唯一标准解法。通过本文的“FRP 封包 -> Nginx 解包 -> 环境变量透传”三步走策略,我们成功在复杂的内网 Docker 环境中还原了用户真实身份。