frp内网穿透http获取客户端真实ip

内网web服务, 通过frp进行内网穿透,客户端真实ip异常. 通过在frp server端之前, 添加nginx进行反向代理, 可以获取到客户端的RealIp, 解决问题

post thumb
Ops
作者 Louis 发表于 2020年7月14日

[TOC]

背景: frp是一款内网穿透神器, 本地的http可以通过frp穿透至外网,详情可以查看官方网站Github. 几经查阅, 对于获取客户端真实ip的配置实在是有点迷茫. 获取不了真实ip, 那么微信回调的接口就会各种报错.

官方的解决

获取用户真实 IP

HTTP X-Forwarded-For

目前只有 http 类型的代理支持这一功能,可以通过用户请求的 header 中的 X-Forwarded-For 来获取用户真实 IP,默认启用。

Proxy Protocol

frp 支持通过 Proxy Protocol 协议来传递经过 frp 代理的请求的真实 IP,此功能支持所有以 TCP 为底层协议的类型,不支持 UDP。

Proxy Protocol 功能启用后,frpc 在和本地服务建立连接后,会先发送一段 Proxy Protocol 的协议内容给本地服务,本地服务通过解析这一内容可以获得访问用户的真实 IP。所以不仅仅是 HTTP 服务,任何的 TCP 服务,只要支持这一协议,都可以获得用户的真实 IP 地址。

需要注意的是,在代理配置中如果要启用此功能,需要本地的服务能够支持 Proxy Protocol 这一协议,目前 nginx 和 haproxy 都能够很好的支持。

这里以 https 类型为例:

# frpc.ini
[web]
type = https
local_port = 443
custom_domains = test.yourdomain.com

# 目前支持 v1 和 v2 两个版本的 proxy protocol 协议。
proxy_protocol_version = v2

只需要在代理配置中增加一行 proxy_protocol_version = v2 即可开启此功能。

本地的 https 服务可以通过在 nginx 的配置中启用 Proxy Protocol 的解析并将结果设置在 X-Real-IP 这个 Header 中就可以在自己的 Web 服务中通过 X-Real-IP 获取到用户的真实 IP。

我这边配置了Proxy Protocol, 直接报错,使用不了, 报的是网络错误, 如果有高手, 可以告诉我这怎么玩.

我的解决思路

架构图

客户端通过阿里云的负载均衡(SLB)访问Nginx服务器, Nginx反向代理至frp server, frp_serverfrp_client通过frp协议实现内网穿透. frp_client配置本地的nginx负载层, nginx负载层流量汇入kubenetes集群. 为用户提供服务.

主机 ip 备注
阿里云SLB 😄
Nginx-web 172.16.111.137 😄
frp_server 172.16.111.129 😄
frp_client 192.168.0.23 😄
Nginx_LB 192.168.0.21 😄
kubernetes-node 192.168.0.30,192.168.0.65,192.168.0.88 😄

SLB后的nginx-web配置

配置入口的nginx的时候, 做ssl 会话卸载. 这样从frp_serverkubenetes集群都可以直接使用http协议. 这层的nginx非常重要, 如果没有, 那么获取的remote_ip 永远都是127.0.0.1或者是内网ip.

upstream frp {
	server 172.16.111.129:80;  # 这个是frp_server的内网ip和http监听端口
}
server {
	listen 80 ;
	server_name frp.fenghong.tech; 
	charset utf-8;
	add_header Content-Security-Policy upgrade-insecure-requests;

	location / {
		proxy_pass http://frp;
		proxy_set_header Host  $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
	}
	access_log  /var/log/nginx/frp.fenghong.tech.access.log  main;
	error_log  /var/log/nginx/frp.fenghong.tech.error.log  info;
}

server {
	listen 443 ssl ;
	server_name frp.fenghong.tech; 
	charset utf-8;
	ssl_certificate  ssl/frp.fenghong.tech;
	ssl_certificate_key ssl/frp.fenghong.tech;

	ssl_session_timeout 5m;
	add_header Content-Security-Policy upgrade-insecure-requests;


	location / {
		proxy_set_header Host  $host;
		proxy_pass http://frp;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
	}
	access_log  /var/log/nginx/frp.fenghong.tech.access.log  main;
	error_log  /var/log/nginx/frp.fenghong.tech.error.log  info;
}

frp_server配置

这个参考官网,也没有什么好说的, 这里是我配置. 仅供参考. 我这边admin端口都开了防火墙的, 只允许内网访问. 可以酌情配置.

[common]
bind_addr = 0.0.0.0
bind_port = 7200
bind_udp_port = 7201
kcp_bind_port = 7200

vhost_http_port = 80
## 如果前端nginx已经做了ssl会话卸载.那么这个https_port可以省略了.
vhost_https_port = 443   

dashboard_addr = 0.0.0.0
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = weakpasswod
log_file = ./frps.log
log_level = debug
log_max_days = 3

token = weakpasswod
max_pool_count = 5
max_ports_per_client = 0
authentication_timeout = 0
tcp_mux = true

frp_client配置

这里贴一下我的配置.

[common]
server_addr = `frp_server的外网ip`  # 自行更改
server_port = 7200
token = weakpassword

[frp.fenghong.tech]
type = http
local_ip = 192.168.0.21
local_port = 80
remote_port = 80
use_encryption = false
use_compression = true
custom_domains = frp.fenghong.tech

本地nginx-LB负载均衡服务配置.

因为我后端是kubernetes集群, 而且service又没有SLB类型的, 采用的clusterip +nginx-ingress暴露本地的服务.这里的nginx相当于负载均衡作用.

upstream k8s-fenghong {
	ip_hash;
	server 192.168.0.30:80;
	server 192.168.0.65:80;
	server 192.168.0.88:80;

}
server {
	listen 80 ;
	server_name frp.fenghong.tech; 
	charset utf-8;
	add_header Content-Security-Policy upgrade-insecure-requests;

	location / {
		proxy_pass http://k8s-fenghong ;
		proxy_set_header Host  $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
	}
	access_log  /var/log/nginx/frp.fenghong.tech.access.log  main;
	error_log  /var/log/nginx/frp.fenghong.tech.error.log  info;
}

后面就是kubernetes集群相关了, 这边就到此为止吧.

查看日志进行验证

nginx日志格式, 可以看到$http_x_forwarded_for已经获取到了我们的客户端的RealIP

$  cat nginx.conf | grep -C log_format

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
		'$status $body_bytes_sent "$http_referer" '
		'"$http_user_agent" "$http_x_forwarded_for" "$proxy_add_x_forwarded_for"';

$ tail -f /var/log/nginx/frp.fenghong.tech.access.log
192.168.0.23 - - [14/Jul/2020:19:00:48 +0800] "POST /api/company/isExists/mobile HTTP/1.1" 200 79 "https://frp.fenghong.tech.access/api/swagger-ui.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36" "124.78.19.227, 172.16.111.137" "124.78.19.227, 172.16.111.137, 192.168.0.23"

感谢frp作者的努力.

参考

Tags: