因为经常需要远程访问自己的机器,所以写一个博客记录一下
公网访问内网机器:基于frp的内网穿透
从公网中访问自己的私有设备向来是一件难事儿。
1. 为什么需要内网穿透?
A. 计算机网络
如何在自己的机器上访问另外一台机器?一台机器本身是一个独立的整体,可以直接访问本身所存储的文件。而如果现在有两台机器的话,机器A想要访问机器B上的文件,该怎么做?
事实上,当两台、乃至多台机器在一起的时候,就构成了一个以单台计算机为节点,以计算机和计算机之间的链接为边的网络,即计算机网络。我们访问另外一台计算机文件,就需要通过计算机网络来实现。
计算机网络中有一个比较重要的问题,就是如何标识每一台计算机。作为人,我们能很简单的知道,这是Jack Wang的Mac、那是Sarah的Windows、那是课题组公用的Linux,但是对于计算机来说,这是很难的。
所以类似于身份证能够识别不同的人一样,给组成计算机网络中的每一台设备一个编号,这个编号就作为每一台机器在网络中的标识,利用这个编号,能够找到需要访问的计算机。这个编号称为IP地址
所以,想要访问另外一台电脑,就需要知道对方的IP地址,然后才能访问。
B. 私网间互相访问
IP地址根据最初的设定,分为ABC几类。直白的来说就是有一些IP地址是保留给局域网使用(私网),剩下的IP地址全球共享(公网)
- 公网IP地址是不能重复的。
- 保留给局域网使用的IP地址在不同的局域网间是可以重复的。例如:你家和我家可能都有
192.168.0.1
这个IP地址,而使用这个IP地址的设备在你的局域网中可能是你的手机,而在我的局域网中可能是我的PC。但是,保留给局域网使用的IP地址在同一个局域网内是不能重复的。例如:我家的192.168.0.1
这个IP地址只可能被一部设备使用,要么是我的手机,要么是我的PC。
因此,不同局域网内的IP地址可重复这一性质就导致了两台处于不同局域内机器互相连接可能存在问题:
- 我们在办公室中使用的PC连接了办公室的网络,因此具有一个私网的IP地址,可能是
192.168.0.1
;我们的手机连接办公室的网络,因此也具有一个私网地址,可能是192.168.0.2
- 我们需要连接的在家里的PC连接了家里的网络,因此具有一个私网的IP地址,可能是
192.168.0.2
那么我们使用在办公室内的机器连接192.168.0.2
这个地址的时候,我们本来想连接家里的PC,但是由于私网的IP地址可重复,我们可能会连接到同一局域网下的另外一台机器。
C. 借助公网设备作为跳板
解决问题的办法就是获得一个公网IP。因为世界上所有的网络在一起,构成了互联网,或者公网。公网IP在公网中是唯一的。所以不管我们处于哪个局域网,都可以访问到具有公网IP的机器。
所以解决方案就是:
- 机器A和机器B都先连接到公网机器。
- 因为连接在建立后通信是双向的,所以在机器A、B连接到公网机器后,可以和公网机器双向通信
- 而后机器A把需要访问的机器B上的文件路径传给公网机器
- 公网机器把文件路径通知给机器B
- 机器B把文件传输给公网机器
- 公网机器再把文件传输给机器A
这样利用公网机器作为跳板,就实现了处于两个私网的机器间的访问。这种解决方案称为内网穿透
,即借助一个具有公网IP的服务器作为跳板,来让处于其他私网的设备访问到内网中的机器。这样做类似于把内网穿透到公网上,因此称为内网穿透
.
D. 哪来的公网IP?
上面解决两个私网机器互相访问最核心的,就是要有一个具有公网IP的机器。而由于中国加入到互联网的时间比较晚,所以分到的公网IP就比较少。原来很早的时候,打电话免费就可以申请得到一个公网IP,但现在运营商因为IP地址短缺不给你分配公网IP地址。
如果花钱买的话,目前市场中常用的网络有移动宽带、联通宽带、电信宽带,不同的网络运营商提供业务办理不同,收取的费用标准也存在一定的不同。一般情况,申请公网IP大概需要1000-2000元,具体的收费标准还需要以当地各大网络运营商提供的具体解决方案为准。所以就很贵。
所以就出现了一些服务商,他们购买了公网IP,然后架设了公网机器,我们只需要购买这些服务商提供的跳板服务/穿透服务就行了。但是这样功能非常有限,只有跳板服务,可玩性和功能性不高。
因此,我们不如直接租一个具有公网IP的服务器,然后自己架设跳板服务/穿透服务即可
2. frp搭建内网穿透
A. 什么是frp
既然我们需要进行内网穿透,肯定就需要有一个程序帮助我们去完成公网、私网机器连接的建立,公网机器上消息的转发等等功能,这个软件就是frp
。
简单地说,frp
就是一个反向代理软件,它体积轻量但功能很强大。利用他就可以实现反向穿透,即可以使处于内网或防火墙后的设备对外界提供服务。
它支持HTTP
、TCP
、UDP
等众多协议。我们今天使用的仅限于TCP
和UDP
,所以使用frp
是足够了
B. 机器配置信息
既然frp
需要建立公网机器和私网机器之间的连接,因此frp实际上是两个程序:
frpc
:即frp
客户端(client
),运行在需要被穿透到公网上的机器,即将来被远程访问的机器frps
:即frp
服务端(server
),运行在具有公网IP的机器上
而在运行的时候,frp
是根据配置文件中的描述来运行的。
因此,我们未来需要在公网机器和被远程访问的机器上分别下载frp
程序,并编写配置文件。
这里我的配置是:
- 公网机器是一台腾讯云服务器,后面就简称为服务器
- 公网地址是
81.68.123.84
- 用户名是
lighthouse
- 主机名是
VM-4-7-ubuntu
- 公网地址是
- 需要被穿透的机器(被其他私网机器访问的机器,即家里的机器)称为PC端
- 私网IP地址不重要
- 用户名是
jack
- 主机名是
jack-Alienware-Aurora-R13
C. 下载frp
1) 服务器下载frps
首先连接进服务器下载frp
服务端
ssh lighthouse@81.68.123.84
mkdir -p ~/opt/frps
cd opt/frps
wget -c https://github.com/fatedier/frp/releases/download/v0.51.0/frp_0.51.0_linux_amd64.tar.gz
注意,不同CPU架构下载的链接是不同的,我的服务器是
Intel
的CPU
,所以是x86_64
架构,下载amd64
版本的程序。你需要根据自己的服务器的架构去下载。所有版本的下载地址:https://github.com/fatedier/frp/releases
然后解压
tar xzvf frp_0.51.0_linux_amd64.tar.gz
ls -al frp_0.51.0_linux_amd64
因为目前是在服务器上配置frp
服务端,因此删去不需要的客户端程序:
rm frp_0.51.0_linux_amd64/frpc*
ls -al frp_0.51.0_linux_amd64
2) PC端下载frpc
步骤和服务器上下载frps
基本是一样的,这不过这里要删除的是frp
服务端
mkdir -p ~/opt/frpc
cd ~/opt/frpc
wget -c https://github.com/fatedier/frp/releases/download/v0.51.0/frp_0.51.0_linux_amd64.tar.gz
tar xzvf frp_0.51.0_linux_amd64.tar.gz
rm frp_0.51.0_linux_amd64/frps*
ls -al frp_0.51.0_linux_amd64
D. 配置frp
类似于下载,配置frp
也是分为PC端配置和服务器配置。
frp
的工作流程如上图。假设现在远程主机需要远程登录PC机,那么实际上就是远程主机使用PC机上的SSH程序,即访问PC机上的22
端口。
所以,当远程主机的一个ssh
请求来了之后:
- 远程主机的
ssh
请求打到服务器上的10001
端口 10001
端口被frps
监听,检测到有一个ssh
请求后,将其通过本机的6006
端口发出去- PC端的
frpc
接收到远程主机发来的ssh
请求后,将其转发到本机的22
端口 - 远程主机的
ssh
请求最终被监听PC端22
端口的服务程序处理
因此:
- PC端(
frp
客户端)需要指定,将自己的22号端口映射为服务器(frp
服务端)的10001
端口,或者说需要将服务器的10001
端口的流量转发到PC端的22
端口 - 服务器(
frp
服务端)需要指定PC机(frp
客户端)需要通过哪个端口和服务器(frp
服务端)通信
可以看到,frp
其实最重要的就是转发了远程主机发来的数据包,因此frp
实际上工作在传输层。稍后我们在配置中就能看到,在frp
的配置文件中,我们需要指定服务器的哪个端口的哪种数据包需要转发到PC端的哪一个端口。
针对上面举的SSH
的例子,SSH
服务本质上是一个文本传输服务,只不过进行了加密,因此SSH
的数据包使用的是TCP
协议。后面穿透不同的服务的时候需要指定数据包的类型
1) 服务器配置
服务器端要配置的,实际上就只有bind_port
。因为listen_port
是需要frp
客户端指定的
ssh lighthouse@81.68.123.84
cd ~/opt/frps/frp_0.51.0_linux_amd64
vim frps.ini
将其中的bind_port
修改为6006
,这里是为了和上面的图配合起来,你也可以改成自己喜欢的。
cat frps.ini
服务器端的配置就结束了,最后得到的配置文件为:
# frps.ini,即服务端上的配置
[common]
bind_port = 6006
2) PC端配置
客户端的配置比服务端配置要复杂些,因为要指定远程端口的某种流量转发到本地的某个端口。
cd ~/opt/frpc/frp_0.51.0_linux_amd64
vim frpc.ini
按照上面介绍的
- 首先设置PC端的
frp
客户端连接到的frp
服务端的IP地址为服务器地址,即设置server_addr=81.68.123.84
- 然后设置PC端的
frp
客户端连接道frp
服务端监听的端口,即设置server_port=6006
接着编写一个新的规则
- 规则名字可以随便给,这里直接就是
Aurora-SSH
- 服务端需要监听服务端的
10001
端口 - 服务端转发的数据包类型就是
tcp
- 服务端转发的数据包的目的地址就是本机地址
127.0.0.1
- 服务端转发的数据包的目的端口就是
22
端口
最后得到的总配置文件为
注意,为了避免你的内网机器被穿透到我的服务器上,你需要修改一下
server_addr
,免得到时候我能登录你的内网机器\^_\^
# frpc.ini,即PC端上的配置
[common]
server_addr = 81.68.123.84
server_port = 6006
[Aurora-SSH]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 10001
所以总的来说就是:将81.68.123.84
服务器上的10001
端口接收到的tcp
数据包转发到本地的22
端口,并且通过服务器的6006
端口进行通信
E. 启动frp
首先启动服务器端的frps
ssh lighthouse@81.68.123.84
cd ~/opt/frps/frp_0.51.0_linux_amd64
./frps -c frps.ini| tee -a frps.log
这里把命令行输出到信息备份一份到frps.log
文件中
然后启动PC端的frpc
cd ~/opt/frps/frp_0.51.0_linux_amd64
./frpc -c frpc.ini | tess -a frpc.log
启动成功后,再检查一下服务器上的frps
,发现已经接收到了frpc
客户端发来的规则,开始监听端口10001
了
F. 验证
最后验证一下能否远程登录到PC端。
这里直接就在PC端下SSH
连接服务器的10001
端口,因为登录的是PC端的用户,所以用户名还需要是PC端的用户名,只不过此时SSH
服务器地址已经变成了服务器的地址
ssh jack@81.68.123.84 -p 10001
3. 番外:PC端开机自运行frpc
最后,服务器因为我们随时可以链接进去,所以使用tmux
开一个session
,保持frps
运行即可。但是PC端则需要我们事先手动运行frpc
。
如果我们运行了一些命令导致PC端重启了,那么再次启动的时候就会导致服务丢失。所以,我们希望PC端在重新启动的时候能够自动运行frpc
。
为此,我们使用Systemd来新建一个服务。关于Systemd后面会写点文章介绍,这里就先用吧。
Systemd介绍:
Systemd是一个用于Linux系统的系统和服务管理器。它被设计为替代传统的SysV初始化系统(init)和System V启动脚本,并提供了更先进的功能和特性。
Systemd的目标是改进系统的启动速度、效率和可靠性,并提供更好的系统管理和服务控制功能。它引入了一种并行启动机制,可以同时启动系统中的多个服务,加快系统启动时间。此外,Systemd还支持基于套接字激活的服务,可以在需要时按需启动或停止服务,提高系统资源利用率。
Systemd还引入了一种统一的单元文件(unit files)格式,用于描述系统服务、设备、挂载点等。这些单元文件位于
/etc/systemd/system/
和/usr/lib/systemd/system/
等目录下,通过这些单元文件,可以对系统的各个方面进行配置和管理。Systemd提供了一系列的命令行工具,用于管理和控制系统和服务,例如
systemctl
命令用于启动、停止、重启和管理系统服务,journalctl
命令用于查看系统日志。总的来说,Systemd是一个现代化的系统和服务管理器,它在Linux系统中扮演着重要的角色,改进了系统的启动和管理方式,提供了更好的性能和功能。它已经成为许多主流Linux发行版的默认初始化系统。
A. 编写启动脚本
首先编写一个脚本来运行frpc
cd ~/opt/frpc
# 创建一个软连接,方便后面更新版本
ln -s frp_0.51.0_linux_amd64 bin
vim start_frpc.sh
内容如下:
#! /bin/bash
run_folder=$(cd $(dirname $0) && pwd || exit)
bin_folder="${run_folder}/bin"
cd ${bin_folder} && ${bin_folder}/frpc -c ${bin_folder}/frpc.ini 2>&1 | tee -a ${run_folder}/frpc.log
运行这个脚本就可以一键化直接运行frpc
了
chmox 755 start_frpc.sh
./start_frpc.sh
B. 开机运行启动脚本
编写完了开机脚本还不行,还需要开机就运行这个脚本。后面就是通过Systemd来设置开机运行这个脚本了。开机自运行的Systemd任务需要放在/usr/lib/systemd/system
目录下
首先新建一个Systemd任务,名字为frpc-client
,而后创建软连接方便修改
cd ~/opt/frpc
sudo touch /etc/systemd/system/frp-client.service
ln -s /etc/systemd/system/frp-client.service ./
sudo vim frp-client.service
然后编写内容如下:
# 这个部分定义了服务的基本信息, 这里是一个启动frpc的服务
[Unit]
Description=开机自动运行frpc任务
# 必须要在网络启动之后才能运行, 因为Systemd是多线程运行服务的
After=network.target
# 同时该任务以来网络, 因此声明启动网络服务是该服务的依赖
Wants=network.target
# 这个部分定义了服务的运行参数和行为
[Service]
# 指定了服务的类型,这里是simple,表示是一个简单的服务,即执行指定的命令或脚本。
Type=simple
# 指定了运行服务的用户,这里是jack,表示以jack的身份运行服务
User=jack
# 无论是由于错误、异常退出还是手动停止,只要服务退出,它都应该立即重新启动。这确保了服务的持续性,即使出现问题导致服务停止,Systemd也会自动重新启动它,以恢复服务的正常运行。指定为always确保了下面的脚本会一直运行
Restart=always
# 指定了服务在重启之前的等待时间
RestartSec=5s
# 指定了服务可以打开的最大文件描述符数,这里是1048576
LimitNOFILE=1048576
# 指定了要运行的命令或脚本的绝对路径
ExecStart=/bin/bash /home/jack/opt/frpc/start_frpc.sh
# 服务重新加载(reload)时执行的命令或脚本。
ExecReload=/bin/bash /home/jack/opt/frpc/start_frpc.sh
# 这个部分定义了服务的安装和启动配置
[Install]
# 下面这段不能少, 强制要求了在用户登录前运行启动脚本
WantedBy = multi-user.target
然后把这个启用这个任务
# 因为新建了一个开机启动任务,所以重新加载一下systemd守护进程
sudo systemctl daemon-reload
sudo systemctl enable frp-client.service
然后查看一下这个任务的状态
sudo systemctl status frp-client.service
因为目前只是加载了这个任务(表示启动时将会运行这个任务),而由于上次启动时还没运行这个任务,所以显示的是dead。
紧着这,启动一下这个服务
sudo systemctl start frpc-client.service
sudo systemctl status frpc-client.service
接下来重启一下PC端就行了,此时这个任务就会被运行
sudo reboot
# 重启后检查一下任务状态
sudo systemctl status frp-client.service