使用 frp 内网穿透
0 背景
在 python onnx_app_pyav.py
时注意到:
Running on local URL: http://127.0.0.1:7860
Running on public URL: https://b68015d75d74ce4815.gradio.live
This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)
Gradio 提供的这个临时 public URL 只能存在三天。这是很不好的。
Gradio 也提供了直接在服务器上使用 Nginx 部署的办法:https://www.gradio.app/guides/running-gradio-on-your-web-server-with-nginx。但是现实问题是网络服务器没有足够的 GPU 算力,而付得起的 GPU 服务器(指学校内网 GPU 平台)没有(甚至说不能设置)公网 IP。在这种情况下该怎么办呢?只能曲线救国。
实际运行 Gradio 的机器只能是内网机器,而本地链接又无法在外网访问。我们需要一种办法,使得在公网上能访问内网机器的服务。
我了解到使用 frp 可以进行和 Gradio 同样的内网穿透,故来实践。
1 VPS
有公网 IP,解析好的域名的 VPS。
最好是国外的 VPS,因为规定是:域名解析到国内的服务器上就需要备案,比较复杂。
我这里准备了一个新加坡的服务器。
2 frp
2.1 工作原理
frp 主要由两个组件组成:客户端 (frpc) 和 服务端 (frps)。通常情况下,服务端部署在具有公网 IP 地址的机器上,而客户端部署在需要穿透的内网服务所在的机器上。
由于内网服务缺乏公网 IP 地址,因此无法直接被非局域网内的用户访问。用户通过访问服务端的 frps,frp 负责根据请求的端口或其他信息将请求路由到相应的内网机器,从而实现通信。
2.2 下载
GitHub 地址:https://github.com/fatedier/frp,在 Release 里下载。
跟随最新就好,这样可以匹配最新的官方文档。https://gofrp.org/
wget https://github.com/fatedier/frp/releases/download/v0.52.3/frp_0.52.3_linux_amd64.tar.gz
tar -xvf frp_0.52.3_linux_amd64.tar.gz
mv frp_0.52.3_linux_amd64 frp
3 通过 SSH 访问内网机器
先从最简单的开始。
3.1 设置 frp
设置参考文档:https://gofrp.org/zh-cn/docs/examples/ssh/
内网机器对应 frpc,VPS 对应 frps。
frp 不限机器的类型,比如客户端 frpc 可以是 Linux 也可以是 Windows。操作基本没有区别。
frps.toml
:
bindPort = 7000 # frp 所在服务器用于接收客户端连接的端口
frpc.toml
:
serverAddr = "xx.xx.xxx.xxx" # frps 所在服务器的公网 IP 地址
serverPort = 7000 # 对应 frps.toml 的 bindPort
[[proxies]]
name = "Gradio" # 名称无所谓
type = "tcp"
localIP = "127.0.0.1" # 内网服务的地址
localPort = 7860 # 内网服务的端口
remotePort = 20000 # frp 服务端监听的端口
3.2 设置 VPS 防火墙
两层:一层防火墙规则(云平台设置),一层防火墙本身,都要修改。
两个端口:bindPort
(serverPort
)和 remotePort
都要开放。
防火墙规则:设置规则,开放两个端口。
防火墙:设置防火墙一般需要 sudo。
Ubuntu:打开防火墙,使用开放指定端口的命令,改完之后重启防火墙。
sudo ufw enable # 启动防火墙:之后选项选 y
sudo ufw allow 22 # 马上 allow 22 的 ssh,其他的还有:7000、80、443 端口等
sudo ufw allow 7000
sudo ufw allow 80
sudo ufw allow 443
sudo ufw allow 20000 # 为后续的 http 访问开一个端口备用
sudo ufw status # 查看开放端口,确认都开启
sudo ufw reload # 重启防火墙
另 CentOS 7 命令参考如下:
systemctl start firewalld # 启动防火墙
firewall-cmd --zone=public --add-port=1935/tcp --permanent # 开放指定端口(端口号自己改)
firewall-cmd --zone=public --remove-port=7860/tcp --permanent #关闭指定端口(端口号自己改)
firewall-cmd --zone=public --list-ports # 查看 firewall 端口
firewall-cmd --reload # 重启 firewall
systemctl stop firewalld.service # 停止 firewall
systemctl disable firewalld.service # 禁止 firewall 开机启动
检查方法:(修改完防火墙之后建议等一小会儿)安装 nmap:apt install nmap -y
nmap xx.xx.xxx.xxx -p remotePort
参数为公网地址和开放端口。端口状态是 filtered 则未放行。(open 和 close 都没事)crul xx.xx.xxx.xxx:remotePort
参数为公网地址和开放端口。
参考:https://github.com/fatedier/frp/issues/982
3.3 frp 运行(测试时多用)
(别忘记先启动你的服务)
先 frps
:
./frps -c ./frps.toml
后 frpc
:
./frpc -c ./frpc.toml
最后,访问:公网 IP + : + remotePort
即可。即 xx.xx.xxx.xxx:remotePort
。
如果有绑定域名的话,还可以将 IP 替换为域名访问。
可能出现的问题:时间不匹配。
参考:https://github.com/fatedier/frp/issues/510
客户端和服务器的时间不对应会导致错误。需要矫正时间。
参考:http://www.mobiletrain.org/about/BBS/150075.html
两边都执行:
sudo timedatectl set-timezone Asia/Shanghai # 中国上海时区,与北京时间保持一致
sudo apt-get install ntp -y # 网络时间协议(NTP)来自动同步系统时间
sudo ntpdate cn.pool.ntp.org # 从中国 NTP 服务器上获取最新的时间,并将系统时间进行更新
sudo hwclock --systohc # 把系统时间同步到硬件时钟中
3.4 systemd 设置 frp 持久运行
一般在测试确认无误之后使用。
文档推荐:https://gofrp.org/zh-cn/docs/setup/systemd/
安装 systemd 不必多说。
创建写入 frps.service
文件:
sudo vim /etc/systemd/system/frps.service
其中 frps
的安装路径需要写绝对路径。可以使用 readlink -f
获取绝对路径。
/etc/systemd/system/frps.service
:
[Unit]
# 服务名称,可自定义
Description = frp server
After = network.target syslog.target
Wants = network.target
[Service]
Type = simple
# 启动 frps 的命令,需修改为您的 frps 的安装路径
ExecStart = /root/frp/frps -c /root/frp/frps.toml
[Install]
WantedBy = multi-user.target
client 也能画蛇添足写出 systemd 的设置,但是考虑到 frpc 可能不会长久启动,所以也可以不设置。
/etc/systemd/system/frpc.service
:
[Unit]
# 服务名称,可自定义
Description = frp client
After = network.target syslog.target
Wants = network.target
[Service]
Type = simple
# 启动 frpc 的命令,需修改为您的 frpc 的安装路径
ExecStart = /root/frp/frpc -c /root/frp/frpc.toml
[Install]
WantedBy = multi-user.target
使用 systemd 命令管理 frps 服务是很方便的:
sudo systemctl start frps # 启动 frp
sudo systemctl stop frps # 停止 frp
sudo systemctl restart frps # 重启 frp
sudo systemctl status frps # 查看 frp 状态
在启动 frp 后,用 sudo systemctl status frps
查看状态。
如果报错、无法运行,可能是由于安全设置缺少运行二进制文件的权限,可使用 chmod +x
;
成功启动输出样例:
frps.service - frp server
Loaded: loaded (/etc/systemd/system/frps.service; disabled; vendor preset: enabled)
Active: active (running) since Fri 2023-10-27 02:36:04 UTC; 4s ago
Main PID: 29298 (frps)
Tasks: 5 (limit: 4915)
CGroup: /system.slice/frps.service
└─29298 /root/frp/frps -c /root/frp/frps.toml
最后设置 frps 开机启动:
sudo systemctl enable frps
4 为本地 HTTP 服务启用 HTTPS
然后我们看 https://gofrp.org/zh-cn/docs/examples/vhost-http/,通过自定义域名访问内网的 Web 服务。这部分大致与 3 中内容相似,不再赘述。区别在于访问内网的 Web 服务通过 HTTP,不再需要设置 frpc 的 remotePort(frp 服务端监听的端口)。
在原来的情况下,当用户访问 remotePort 时,这个端口上的流量会被直接转发到对应的本地服务,因此需要明确指定 remotePort;而现在基于 HTTP 的反向代理根据 HTTP 请求的内容(例如域名、URL 路径等)将请求转发到对应的本地服务,因此不需要设置 remotePort。
但是前面的访问方法,既带了端口,又没有 https,这很不好,让我们来改进一下。
有官方文档:https://gofrp.org/zh-cn/docs/examples/https2http/,提到的办法是只使用 frp 自身来进行的操作。从结论来说,文档中的办法并不是最好的办法。
实际上还有使用 frp + Nginx 的组合操作。在 frp 单独使用的时候,frp 可以使用代理服务器的 80 和 443 端口;frp + Nginx 的方法中,Nginx 则占用了这两个端口,故 frp 只能转而使用其他端口。但不过不得不说 Nginx 才是专业的,把 443 端口给他用很正确。使我意识到这一点的是:https://www.cnblogs.com/shook/p/12790532.html,写的十分好的一篇文章。
4.1 共通:证书申请
不管是上面提到的哪种方法,都需要申请 SSL 证书。这里我们使用免费的 Let’s Encrypt。
实际证书的申请和平台无关,故而我们可以在 Linux、Windows 上生成。
Linux 方法
先安装。
sudo apt update
sudo apt upgrade -y
sudo apt install -y certbot # 证书申请工具
证书申请命令:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
--server https://acme-v02.api.letsencrypt.org/directory \
-m sorrow-time@outlook.com \
-d "i4.work, *.i4.work"
--manual
交互式获取--preferred-challenges dns
使用 DNS 验证的方式(泛域名只能使用 DNS 验证)--server
指明支持 acme-v02 的 Server 地址-m
输入邮箱-d
同时申请带有通配符和不带通配符的域名!
申请过程中需要进行 TXT 的 DNS 解析,下面是示例:
Please deploy a DNS TXT record under the name
_acme-challenge.i4.work with the following value:
nrmd1SOtdH0WFuzbBWy7Cj7NMEw7XIc_I772-yTz1rE
Before continuing, verify the record is deployed.
我是到阿里云去操作的,求稳可以等到 DNS 解析刷新结束。
结束后证书位置:下载其中 fullchain.pem
和 privkey.pem
保存留用。后缀名是什么并不重要,不管是 .key
还是 .crt
的都无所谓。
/etc/letsencrypt/live/i4.work/
证书更新:
先输入:
crontab -e # 定时任务
然后写入(可选 vim 操作):
30 1 10 * * /usr/bin/certbot renew
保存退出。
更详细可参考:https://www.cnblogs.com/wzlinux/p/11188454.html
Windows 方法
参考:https://zhuanlan.zhihu.com/p/627526278
Windows 不让 \ 换行,一行版:
certbot certonly -d "i4.work, *.i4.work" -m sorrow-time@outlook.com --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory
别的参考:https://zhuanlan.zhihu.com/p/438161610
4.2 法 1:frp 自身进行反代
先大致描述情景:Windows 一台作为内网机器(客户端),公网 Linux VPS 一台作为代理(服务端)。
证书仅位于内网机器(客户端)。
VPS 仅需要开放防火墙、配置 frps.toml
即可。
bindPort = 7000
vhostHTTPSPort = 443
内网机器上需要:准备好 SSL 证书和私钥、配置 frpc.toml
。
serverAddr = "xx.xx.xxx.xxx"
serverPort = 7000
[[proxies]]
name = "test_htts2http"
type = "https"
customDomains = ["i4.work"]
[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:7860"
# HTTPS 证书相关的配置
crtPath = "./fullchain.pem"
keyPath = "./privkey.pem"
hostHeaderRewrite = "127.0.0.1"
requestHeaders.set.x-from-where = "frp"
然后先后运行。
4.3 衔接:Nginx 设置
完成法 1 之后可以使用 Nginx 来解决两个小问题:
- http 跳转到 https
- www 跳转到不带 www(或者相反)
- 安装 Nginx
sudo apt install -y nginx
关于 Nginx,不同的安装方式,Nginx 的位置也不一样。本次教程中位于 /etc/nginx/
。
- Nginx 使用 systemd 管理
sudo systemctl status nginx # 查看状态
sudo systemctl stop nginx # 停止
sudo systemctl start nginx # 启动
sudo systemctl restart nginx # 强制重启
nginx -t # 检查格式
- Nginx 配置基础知识——自行了解。
建议先学习基本的 Nginx 的配置的知识,对配置有基本的认识。
例如:
总配置 etc/nginx/nginx.conf
中,最后 include 的部分:
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
我们不直接在该文件中写入配置,而是修改、添加这些 include 中的文件。
- 实际解决
方法:
sudo vim /etc/nginx/conf.d/i4.work.conf
写入:
server {
# Force redirect to https
if ($scheme != "https") {
return 301 https://$server_name$request_uri;
}
if ($host = 'www.i4.work' ) {
return 301 $scheme://i4.work$request_uri;
}
}
之后检查格式,并重启 Nginx:
nginx -t
sudo nginx -s reload
- 最后验证
我们希望的结果:四个 url 都能跳转到 https://i4.work/
即成功。
https://i4.work/
http://i4.work/
https://www.i4.work/
http://www.i4.work/
4.4 法 2:Nginx + frp 配置
给出图示:
证书仅位于公网 VPS(服务端)。
可以在完成 4.1 ~ 4.3 的基础上修改部分内容。
内网机器上需要:仅配置 frpc.toml
。
serverAddr = "xx.xx.xxx.xxx"
serverPort = 7000
[[proxies]]
name = "web"
type = "http"
localPort = 7860
customDomains = ["i4.work"]
VPS 需要:
- 再为 http 开放一个端口(如果在 3.2 没操作就要进行了)
- 配置
frps.toml
。
bindPort = 7000
vhostHTTPPort = 20000
- 将证书和密钥放到
/etc/nginx/cert
目录下。该目录还不存在,故而使用mkdir
。
mkdir /etc/nginx/cert
cp fullchain.pem /etc/nginx/cert/fullchain.pem
cp privkey.pem /etc/nginx/cert/privkey.pem
- 补充配置:
/etc/nginx/conf.d/i4.work.conf
,最后结果:
server {
listen 80;
listen 443 ssl;
# Force redirect to https
if ($scheme != "https") {
return 301 https://$server_name$request_uri;
}
if ($host = 'www.i4.work' ) {
return 301 $scheme://i4.work$request_uri;
}
server_name i4.work;
ssl_certificate cert/fullchain.pem;
ssl_certificate_key cert/privkey.pem;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:20000; # 反向代理
# 映射的 frp 服务端 frps.toml 的 vhostHTTPPort 端口
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_max_temp_file_size 0;
proxy_redirect off;
proxy_read_timeout 240s;
}
}
- 重载 Nginx 配置。
nginx -t
sudo nginx -s reload
然后先后运行测试一下。验证方法同上。
一点发现
似乎等待大文件处理完毕(同步)的 error 消失了!这真是惊奇的发现。但是我并不知道是为什么。
更新:猜测可能是 Gradio 的共享链接做出了限制,而使用本地链接访问没有这种限制,顺带着内网穿透也没有限制了。
2 条评论
[...]又一次 frp 反代记录 & SSL 证书签发心得SSL 证书签发心得参考:使用 frp 内网穿透 - 珅鸟玄's Blog (sheniao.top)目的和本篇类似,都是使用 frp 进行内网穿透,但是具体写了 frp 的使用方法,具体内容上比本篇详细,然而文章结构上不可避免的不如本篇清晰。建议对照参考。恢复网站 - 珅鸟玄's Blog (sheniao.top)目的和本篇不同,但是使用了 a[...]
[...]又一次 frp 反代记录 & SSL 证书签发心得SSL 证书签发心得参考:使用 frp 内网穿透 - 珅鸟玄's Blog (sheniao.top)恢复网站 - 珅鸟玄's Blog (sheniao.top)重要参考:用 acme.sh 帮你免费且自动更新的 HTTPS 证书,省时又省力 - 知乎 (zhihu.com)如何在 Ubuntu 22.04 上安装、配置、使用 Nginx?_ub[...]