前言
因为公司属于良心型公司,几乎不加班。于是乎,不爱加班的我天天回家便各种躺平。但是出于程序猿的自我危机感,还是决定回家后稍微蹦跶下吧。其实早在毕业前就想蹦跶了,但是因为写(~比~)毕(~较~)设(~懒~)所以没行动起来。
至于为何会自己在家捣鼓起FRP,这就需要去问我精致的广东男孩同事甲
具体起因是因为我的title是云计算工程师,然后又想折腾点东西,便选择了折腾Cloud比较常用的管理云平台中多个主机上的容器化的应用的工具 -- k8s。
因为部署k8s我需要linux系统,就装了台Ubuntu 20.04虚拟机;因为虚拟机所在宿主机不好用,便配置了虚拟机ssh环境和mac上的远程终端工具Termius,让我可以在mac上操作虚拟机;因为我想在公司使用mac能ssh处在家中内网环境的虚拟机,便要做VPN代理或者内网穿透;因为甲建议内网穿透或者找收费内网穿透服务,那肯定自己做内网穿透啊,于是甲给我推荐了FRP ~_~。
为什么会是FRP
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
- 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。
- 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。
- 代理组间的负载均衡。
- 端口复用,多个服务通过同一个服务端端口暴露。
- 多个原生支持的客户端插件(静态文件查看,HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。
- 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。
- 服务端和客户端 UI 页面。
以上内容来自FRP官方文档简介,经过整个部署过程和使用体验,frp确实具有这些功能特性,同时最重要的特性便是部署是真的简单,甚至可以说是无脑,具体见下文。
开始部署FRP
根据官网教程去github release页面下载了最新版本的linux amd64 二进制文件。下载后便是解压了,然后将解压的文件夹拷入阿里云公网服务器(xxx.xxx.xxx.xxx)和内网虚拟机(192.168.3.54)内了。
scp -r frp_0.37.0_linux_amd64/ hh@192.168.3.54:/home/hh/
scp -r frp_0.37.0_linux_amd64/ root@xxx.xxx.xxx.xxx:/root/
然后ssh进入公网服务器内,将刚拷入的frp_0.37.0_linux_amd64
的文件夹中内frpc相关文件删除,因为frpc为客服端文件,也就是我们虚拟机该跑的东西。
cd /root/frp_0.37.0_linux_amd64
rm -rf frpc*
cd systemd
rm -rf frpc*
删除公网服务器内多余的frp文件后,配置frpc.ini
,设置了 frp 服务器用户接收客户端连接的端口,内容如下:
[common]
bind_port = 7000
开始配置systemd 服务。使用systemd的原因是为了服务可以常驻在服务器内,开机后服务也会自动启动。
首先建需要用到的配置和脚本命令软链接到它该在的地方,注意软链接时必须用绝对路径。
cd /root/frp_0.37.0_linux_amd64
ln -s /root/frp_0.37.0_linux_amd64/frps /usr/bin/frps
sudo mkdir -p /etc/frp/
ln -s /root/frp_0.37.0_linux_amd64/frps.ini /etc/frp/frps.ini
ln -s /root/frp_0.37.0_linux_amd64/systemd/frps.service /lib/systemd/system/frps.service
软链接都做完后,便可以载入systemd服务设置开机自启,并启动它了。
sudo systemctl daemon-reload
sudo systemctl enable frps.service
sudo systemctl start frps.service
到这里公网服务器部分的frp便部署好了。然后便是按照上面操作配置内网虚拟机的frpc。
# 删除grps
cd /home/hh/frp_0.37.0_linux_amd64
rm -rf frps*
cd systemd
rm -rf frps*
# 配置frpc.ini 内容如下:
[common]
server_addr = xxx.xxx.xxx.xxx
server_port = 7000
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000
# 进行软链接
ln -s /home/hh/frp_0.37.0_linux_amd64/frpc /usr/bin/frpc
sudo mkdir -p /etc/frp/
ln -s /home/hh/frp_0.37.0_linux_amd64/frpc.ini /etc/frp/frpc.ini
ln -s /home/hh/frp_0.37.0_linux_amd64/systemd/frpc.service /lib/systemd/system/frpc.service
# 载入systemd服务
sudo systemctl daemon-reload
sudo systemctl enable frpc.service
sudo systemctl start frpc.service
到这里FRP便部署好了,下面便可以开始测试了
内网穿透测试
FRP部署好后,测试还是很简单的,只需使用ssh,看是否能连接公网服务器的公网IP 6000端口,命令如下:
ssh -oPort=6000 hh@xxx.xxx.xxx.xxx
然后很不幸的是连接失败,猜测是内网虚拟机可能无法连接公网服务器7000端口。
先进入到内网虚拟机中查看frpc.service
的状态
sudo systemctl status frps.service
2021/07/07 22:02:14 [W] [service.go:104] login to server failed: dial tcp xxx.xxx.xxx.xxx:7000: connect: connection timed out
dial tcp xxx.xxx.xxx.xxx:7000: connect: connection timed out
发现输出果然显示无法连接公网IP 7000端口,使用telnet xxx.xxx.xxx.x 7000
进行验证发现确实无法连接,应该是公网服务防火墙没有开放7000端口的原因,使用以下命令进行开发,顺带把6000端口一并开放:
firewall-cmd --zone=public --add-port=7000/tcp --permanent
firewall-cmd --zone=public --add-port=6000/tcp --permanent
firewall-cmd --reload
因为我的公网服务器是阿里云的,配置有安全组规则,所以去阿里云控制台修改了下安全组规则,对6000,7000端口进行暴露,然后再进行测试。
ssh -oPort=6000 hh@xxx.xxx.xxx.xxx
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.8.0-59-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
156 updates can be installed immediately.
0 of these updates are security updates.
To see these additional updates run: apt list --upgradable
Your Hardware Enablement Stack (HWE) is supported until April 2025.
Last login: Thu Jul 8 00:37:26 2021 from 192.168.3.55
hh@ubuntu:~$
测试完成,到此FRP部署正式完成,内网穿透完成,完结撒花✿✿ヽ(°▽°)ノ✿。
后记
后续遇到的问题
- 多台内网服务器需要对外网暴露时,不允许有proxy名重复。也就是
frpc.ini
文件中的[ssh]
,不能全部都叫[ssh]
,否则会出现[ssh] start error: proxy name [ssh] is already in use
错误,可以换个名字,例如[ssh_r4b]
。
One comment
可不敢随便在公司内网上打洞呀<mjx-container class="MathJax CtxtMenu_Attached_0" jax="CHTML" tabindex="0" ctxtmenu_counter="9" style="font-size: 113.1%; position: relative;"><mjx-math class="MJX-TEX" aria-hidden="true"><mjx-texatom texclass="ORD"><mjx-mo class="mjx-n"><mjx-c class="mjx-cB4"></mjx-c></mjx-mo></mjx-texatom><mjx-mi class="mjx-i"><mjx-utext variant="italic" style="font-size: 88.4%; padding: 0.848em 0px 0.226em; font-family: MJXZERO, serif; font-style: italic;">இ</mjx-utext></mjx-mi><mjx-mi class="mjx-i"><mjx-utext variant="italic" style="font-size: 88.4%; padding: 0.848em 0px 0.226em; font-family: MJXZERO, serif; font-style: italic;">皿</mjx-utext></mjx-mi><mjx-mi class="mjx-i"><mjx-utext variant="italic" style="font-size: 88.4%; padding: 0.848em 0px 0.226em; font-family: MJXZERO, serif; font-style: italic;">இ</mjx-utext></mjx-mi><mjx-mi class="mjx-i"><mjx-utext variant="italic" style="font-size: 88.4%; padding: 0.848em 0px 0.226em; font-family: MJXZERO, serif; font-style: italic;">`</mjx-utext></mjx-mi></mjx-math><mjx-assistive-mml unselectable="on" display="inline"><math xmlns="http://www.w3.org/1998/Math/MathML"><mrow data-mjx-texclass="ORD"><mo data-mjx-pseudoscript="true">´</mo></mrow><mi>இ</mi><mi>皿</mi><mi>இ</mi><mi>`</mi></math></mjx-assistive-mml></mjx-container>