问题背景

最近在配置FRP反向代理服务时,为了让后端服务能够获取到客户端的真实IP地址,我按照常规做法在FRP服务端配置了proxy_protocol_version = v2,并在OpenResty配置中添加了 set_real_ip_from real_ip_header 等指令。配置完成后,网站功能一切正常,客户端IP也能正确获取,但OpenResty的监控系统却开始频繁报错。

错误现象

错误代码:服务内部错误: Get "http://127.0.0.1/nginx_status": EOF

报错截图,OpenResty监控完全崩溃

排查过程

理解PROXY协议

首先我查阅了PROXY协议的文档,了解到这是HAProxy开发的一种协议,用于在代理服务器和后端服务器之间传递客户端原始信息。樱花FRP支持v1、v2和simple三个版本的PROXY协议,我使用的是v2版本协议。

检查配置

检查我的站点OpenResty配置:

listen 80 proxy_protocol default_server; 
    listen [::]:80 default_server; 
    listen 443 ssl http2 proxy_protocol default_server; 
    listen [::]:443 ssl http2 default_server; 
    ...
    real_ip_header proxy_protocol; #  启用 PROXY 协议支持
    real_ip_recursive on; #  递归处理 PROXY 协议中的 IP 地址
    set_real_ip_from 127.0.0.1; #  这里的127.0.0.1表示可信的代理服务器 IP 地址
    ...

FRP服务端的配置:

[common]
proxy_protocol_version = v2

看起来配置是正确的,但为什么监控会报错?

分析监控组件

我使用的OpenResty是1Panel运维面板提供的,查看其文档发现它默认不支持PROXY协议。当监控请求到达时,监控模块尝试解析请求头,但遇到了PROXY协议头而不是常规的HTTP请求头,因此报错。

解决方案

为FRP流量单独配置监听端口

因为OpenResty监控流量来源于127.0.0.1:80这个地址和端口且1Panel的OpenResty只占用80和443端口,所以我只需要避免在这个80端口启用PROXY协议就行,因此我们可以有两种做法。

这两种做法的前提是配置穿透地址为本地IP127.0.0.1端口为443:

FRP配置

  1. 网站服务的443端口启用PROXY协议,80端口保持默认配置不变。

server {
    listen 80 default_server; 
    listen [::]:80 default_server; 
    listen 443 ssl http2 proxy_protocol default_server; #  因为樱花FRP只提供IPV4穿透,所以这需要监听IPV4的443端口 proxy_protocol
    listen [::]:443 ssl http2 default_server; 
    ...
    real_ip_header proxy_protocol; #  启用 PROXY 协议支持
    real_ip_recursive on; #  递归处理 PROXY 协议中的 IP 地址
    set_real_ip_from 127.0.0.1; #  这里的127.0.0.1表示可信的代理服务器 IP 地址
    ...
  1. 单独监听127.0.0.1:443这个地址,将其作为FRP的专属监听配置(更推荐这种做法)。

将下面这几行代码添加到网站的server块中:

listen 127.0.0.1:443 ssl http2 proxy_protocol; #  这里监听FRP代理的本地端口
real_ip_header proxy_protocol; #  启用 PROXY 协议支持
real_ip_recursive on; #  递归处理 PROXY 协议中的 IP 地址
set_real_ip_from 127.0.0.1; #  这里的127.0.0.1表示可信的代理服务器 IP 地址

示例:

server {
    listen 80 default_server; 
    listen [::]:80 default_server; 
    listen 443 ssl http2 default_server; 
    listen [::]:443 ssl http2 default_server; 

    listen 127.0.0.1:443 ssl http2 proxy_protocol; #  这里监听FRP代理的本地端口 proxy_protocol

    proxy_set_header X-Real-IP $remote_addr; 

    ...
    real_ip_header proxy_protocol; #  启用 PROXY 协议支持
    real_ip_recursive on; #  递归处理 PROXY 协议中的 IP 地址
    set_real_ip_from 127.0.0.1; #  这里的127.0.0.1表示可信的代理服务器 IP 地址
    ...

    proxy_http_version 1.1; 

为监控单独配置监听端口

修改网站配置文件,在配置文件最末端添加下列server块(一定要在末端添加这个server块,不然1Panel内网站的基本设置会有部分失效

server {
    listen 127.0.0.1:80; 
    server_name localhost; 
    location /nginx_status {
        stub_status on; # 启用状态模块
        access_log off; # 关闭访问日志
        allow 127.0.0.1; # 仅允许本机访问
        deny all; # 禁止其他IP
    }
}

配置文件总览:

server {
    listen 80 proxy_protocol default_server; 
    listen [::]:80 default_server; 
    listen 443 ssl http2 proxy_protocol default_server; 
    listen [::]:443 ssl http2 default_server; 
    ...

    real_ip_header proxy_protocol; #  启用 PROXY 协议支持
    real_ip_recursive on; #  递归处理 PROXY 协议中的 IP 地址
    set_real_ip_from 127.0.0.1; #  这里的127.0.0.1表示可信的代理服务器 IP 地址
    ...

#  粘贴在配置文件最末端
server {
    listen 127.0.0.1:80; 
    server_name localhost; 
    location /nginx_status {
        stub_status on; # 启用状态模块
        access_log off; # 关闭访问日志
        allow 127.0.0.1; # 仅允许本机访问
        deny all; # 禁止其他IP
    }
}

server块配置截图

验证结果

可以看到通过配置这个server块之后,OpenResty已经可以正常监控全部的网站服务了。

经验总结

  1. 协议兼容性:在引入新协议时要考虑所有相关组件的兼容性

  2. 监控隔离:监控端点最好与业务端点分离,避免相互影响

  3. 逐步排查:从错误日志出发,逐步分析各组件的行为,能更快定位问题

希望这篇折腾日记能帮助遇到类似问题的朋友。如果你有更好的解决方案,欢迎在评论区分享!