云服务器配置的frp服务端,内网机器部署了frp客户端,客户端再把流量传给NGINX。
发现NGINX的access_log打印的客户端ip都是我客户端frp客户的服务ip,不是真实客户的ip地址,想做一些统计分析或者其他工作感觉不是很方便,所以想能有什么办法能拿到客户端的真实ip。
查阅官方文档,有这样一段话
Get Real IP
HTTP X-Forwarded-For
This feature is for http proxy only.
You can get user's real IP from HTTP request headers X-Forwarded-For and X-Real-IP.
大概意思,如果你用的http代理模式,frp默认会把用户的真实ip传给你的服务,通过头 X-Forwarded-For and X-Real-IP。哟呵?默认就传了,然后我尝试定义一个日志头,把我NGINX的x-real-ip头设置注释掉,然后在新日志头打印出来$x-real-ip,发现不行。难道哪里配置错了吗?
log_format main_real_ip '$x-real-ip - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main_real_ip;
这里说下NGINX日志的语法,有2个语法,一个是 log_format 是定义日志格式的, 后面接 格式名字 和格式。还有个是 access_log 是用来定义日志路径和样式的,后面接路径 空格样式名称;
发现这样并不行,不知道哪里出了问题。于是乎查了点资料,发现frp支持一个高级的玩意,叫 Proxy Protocol ,据说这玩意是工作在tcp层的,不管是不是http都行,NGINX也支持。
# frpc.ini
[web]
type = https
local_port = 443
custom_domains = test.example.com
# now v1 and v2 are supported
proxy_protocol_version = v2
只需要在你的frpc的配置里面加一行 proxy_protocol_version = v2即可支持。
等待我配置之后,发现并不能访问,还报了个ssl的错误。查了资料发现,NGINX也需要相关配置的。
首先检查你的NGINX支不支持这个协议
nginx -V 2>&1 | grep -- 'http_realip_module'
如果不支持,要去编译了,我的是支持的。
然后需要在NGINX上加一点点配置。
http {
#...
server {
listen 80 proxy_protocol;
listen 443 ssl proxy_protocol;
#...
}
}
需要在之前的listen后加个 proxy_protocol
然后改下你的日志格式
http {
#...
log_format combined_proxy_addr '$proxy_protocol_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
}
access_log /var/log/nginx/access.log combined_proxy_addr;
经过测试,能够正确打印客户端的真实ip了,收工。
相关资料:
https://www.fenghong.tech/blog/ops/frp-get-realip/
https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/