使用 FRP 进行内网穿透
1. FRP 简介
FRP 是一个由 Go 编写的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等协议。它可以轻松地进行内网穿透,对外网提供服务。其 GitHub 仓库为 fatedier/frp。
FRP 分为客户端和服务端分别称为 FRPC 和 FRPS,客户端用于连接内网的应用程序,服务端用于提供代理服务。
FRP 还提供管理界面,可以通过浏览器访问管理界面,查看代理服务的状态,可以查看每日的流量情况。
大多数情况下我们只需要 TCP 代理即可,HTTP/HTTPS 代理可以支持更多功能,但是需要配置更多参数。
2. Docker 部署
比较流行的 FRP 镜像是由 @snowdreamtech 提供的,包括 snowdreamtech/frpc 和 snowdreamtech/frps 两个镜像。
拉取镜像:
docker pull snowdreamtech/frpc:0.61.1
docker pull snowdreamtech/frps:0.61.1
配置文件内可以使用类似大括号加 .Envs.NAME
的格式可以读取环境变量。
2.1 客户端配置
docker-compose.yml
配置如下:
services:
frpc:
image: snowdreamtech/frpc:0.61.1
container_name: frpc
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./frpc.toml:/etc/frp/frpc.toml:ro
environment:
FRP_SERVER_ADDR: ${FRP_SERVER_ADDR}
FRP_SERVER_PORT: ${FRP_SERVER_PORT}
FRP_SERVER_TOKEN: ${FRP_SERVER_TOKEN}
以 SSH 服务穿透为例,新建 frpc.toml
配置文件,内容如下:
[common]
server_addr = {{ .Envs.FRP_SERVER_ADDR }}
server_port = {{ .Envs.FRP_SERVER_PORT }}
token = {{ .Envs.FRP_SERVER_TOKEN }}
[app_22]
type = "tcp"
remote_port = 222 # 服务端端口
local_ip = 192.168.xxx.xxx # 本地 IP
local_port = 22 # 本地端口
配置远程 FRPS 服务地址到 .env
文件中:
FRP_SERVER_ADDR=frps.example.com
FRP_SERVER_PORT=9870
FRP_SERVER_TOKEN=password
启动服务,即可代理 192.168.10.100:22
到 FRP_SERVER_ADDR:222
。
docker compose up -d
2.2 服务端配置
docker-compose.yml
配置如下:
services:
frps:
image: snowdreamtech/frps:0.61.1
container_name: frps
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./frps.toml:/etc/frp/frps.toml
ports:
- 7890:7890
- 9870:9870
environment:
FRP_SERVER_PORT: 9870
FRP_SERVER_TOKEN: ${FRP_SERVER_TOKEN}
FRP_ADMIN_PORT: 7890
FRP_ADMIN_USER: ${FRP_ADMIN_USER}
FRP_ADMIN_PASS: ${FRP_ADMIN_PASS}
新建 frps.toml
配置文件,内容如下:
[common]
bind_port = {{ .Envs.FRP_SERVER_PORT }}
token = {{ .Envs.FRP_SERVER_TOKEN }}
dashboard_addr = 0.0.0.0
dashboard_port = {{ .Envs.FRP_ADMIN_PORT }}
dashboard_user = {{ .Envs.FRP_ADMIN_USER }}
dashboard_pwd = {{ .Envs.FRP_ADMIN_PASS }}
配置密钥等信息到 .env
文件中:
FRP_SERVER_TOKEN=password
FRP_ADMIN_USER=admin
FRP_ADMIN_PASS=password
启动服务,即可代理客户端请求,注意需要将客户端的端口映射出来。可以通过 HOST 网络或共享网络的方式共享服务。
使用如下方法启用 HOST 模式:
services:
frps:
network_mode: host
如果端口确定,建议不使用 HOST 模式,使用端口映射确保主机安全。
3. FRP 获取真实 IP
3.1 HTTP 代理
使用 HTTP 代理时,可以通过 X-Forwarded-For
头部获取真实 IP。但是由于 HTTP 代理不够灵活,我们一般直接使用 TCP 代理。
3.2 TCP 代理
FRP 支持通过 Proxy Protocol 协议来传递经过 FRP 代理的请求的真实 IP,此功能支持所有以 TCP 为底层协议的类型,不支持 UDP。[1]
通过指定 proxyProtocolVersion
可以开启 Proxy Protocol,下面是配置示例:
serverAddr = {{ .Envs.FRP_SERVER_ADDR }}
serverPort = {{ .Envs.FRP_SERVER_PORT }}
token = {{ .Envs.FRP_SERVER_TOKEN }}
[app]
type = "tcp"
remotePort = 4090
localIp = {{ .Envs.APP_HOST }}
localPort = 4090
proxyProtocolVersion = "v2"
Nginx 支持 Proxy Protocol,可以通过 proxy_protocol
指令来开启。
在 listen
指令中添加 proxy_protocol
参数,然后在 location
指令中添加 proxy_set_header
指令来设置 X-Real-IP
和 X-Forwarded-For
头部。
server {
listen 4090 proxy_protocol;
server_name www.xxxxxx.com;
client_max_body_size 128m;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ @router;
index index.html index.htm;
}
location @router {
rewrite ^.*$ /index.html last;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
}
}
获取用户真实 IP,FRP 文档,https://gofrp.org/zh-cn/docs/features/common/realip/ ↩︎