[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_server
与frp_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_server
到kubenetes
集群都可以直接使用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
作者的努力.