FRP内网穿透实现远程访问内网主机

本文最后更新于:1 年前

关于内网穿透

内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。下面就相互通信的主机在网络中与 NAT 设备的相对位置介绍内网穿透方法。

为什么要使用内网穿透

为了外网要访问内网,因为当不在同一局域网内,ip和地址互相ping不同的话,最简单的方式是使用向日葵与teamview,但是用起来并不方便。

使用frp进行内网穿透

关于frp的介绍

frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。

准备工作

在使用frp之前,需要一台有公网IP的服务器(外网主机),一台需要实现内网穿透的机器(内网主机),SSH工具,及一个域名。

进行配置

服务端配置

下载源文件

1
wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz

解压

1
tar -zxvf frp_0.33.0_linux_amd64.tar.gz

进入解压目录

1
cd frp_0.33.0_linux_amd64/

配置文件

1
vi frps.ini

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#基础配置
[common]
# frp监听的端口,默认为7000
bind_port = 7000
# 授权码
token = 52010

# frp后台管理端口
dashboard_port = 7500

# frp后台管理登陆的账号密码,可自定义
dashboard_user = admin
dashboard_pwd = admin
enable_prometheus = true

# 进行frp日志配置
log_file = /var/log/frps.log
log_level = info
log_max_days = 3

# SSH安全登录配置
[secret_ssh_visitor]
type = stcp
role = visitor
server_name = secret_ssh
sk = 123456
bind_addr = 127.0.0.1
bind_port = 8000

设置与启动frp服务

1
2
3
4
5
6
7
8
9
10
11
sudo mkdir -p /etc/frp
sudo cp frps.ini /etc/frp
sudo cp frps /usr/bin
sudo cp systemd/frps.service /usr/lib/systemd/system/

# 开机自启动
sudo systemctl enable frps
# 启动frp服务
sudo systemctl start frps
# 重新启动frp服务
sudo systemctl restart frps

防火墙开放端口, 根据自身服务器设置,以阿里云为例,如果设置了访问规则,则要放开7000、75000、8000三个端口

1
2
3
4
5
6
7
8
# 添加监听端口
firewall-cmd --permanent --add-port=7000/tcp
# 添加管理后台端口
firewall-cmd --permanent --add-port=7500/tcp
# 添加SSH登录端口
firewall-cmd --permanent --add-port=8000/tcp
# 重载firewall配置
firewall-cmd --reload

验证是否成功

在浏览器中输入:http://服务器的ip:后台管理端口 (后台管理端口的设置在配置文件中) 进入之后会显示登陆需要使用的账号密码,同样的也是在配置文件frps.ini中
FRPS后台


客户端部署(以树莓派为例)

下载源文件

1
wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz

解压

1
tar -zxvf frp_0.33.0_liux_amd64.tar.gz

进入解压目录

1
cd frp_0.33.0_linux_amd64/

配置文件

1
vi frpc.ini

修改配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#基础配置
[common]
# 服务器的ip地址
bind_addr = 0.0.0.0
# 客户端与服务端进行通信的端口,即frp服务端口,需与客户端server_port一致
bind_port = 7000
# 如果是使用了国外服务器,需要使用此行关闭时间戳校验
authentication_timeout = 0
# 授权码
token = 52010

# SSH安全登录配置
[secret_ssh]
type = stcp
# 只有 sk 一致的用户才能访问到此服务
sk = 123456
# 内网服务器ip
local_ip = 127.0.0.1
local_port = 22

在frp_0.33.0_linux_amd64目录下,临时启动命令

1
./frpc -c ./frpc.ini

设置为开机自动启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sudo mkdir -p /etc/frp
sudo cp frpc.ini /etc/frp
sudo cp frpc /usr/bin
sudo cp systemd/frpc.service /usr/lib/systemd/system/

# 开机自启动
sudo systemctl enable frpc

#启动frpc
sudo systemctl daemon-reload
sudo systemctl start frpc

# 重新启动frp服务
sudo systemctl restart frpc

frpc.ini进一步部署

服务端部署(Linux VPS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[common]
bind_addr = 0.0.0.0
# 客户端与服务端进行通信的端口,即frp服务端口,需与客户端server_port一致
bind_port = 7000

# 特权模式密钥,需与客户端frpc.ini一致
privilege_token = 123456

# http服务端口,开启后服务端完成通过域名访问部署于内网的 Web 服务部署,这里将 HTTP 访问端口设为 8080
vhost_http_port = 8080
# https服务端口
vhost_https_port = 443

# 控制台端口 通过 Dashboard 可以方便的查看 FRP 的状态以及代理统计信息展示 通过 http://[server_addr]:7500 访问 Dashboard 界面,用户名密码默认都为 admin。
dashboard_port = 7500

客户端部署(后附每个穿透方案的详解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[common]
server_addr = your_server_ip #VPS服务器IP
server_port = 7000 #端口,与服务端bind_port一致
privilege_token = 123456 #身份验证,自定义,需和服务端frps.ini的一致

login_fail_exit = false #失败时自动重连

admin_addr = 127.0.0.1 #开启后可通过热加载方式进行 FRP 客户端配置变更
admin_port = 7400 #可以通过 frpc reload 命令来动态加载配置文件,通过 frpc status -c ./frpc.ini 命令在 FRP 客户端查看当前代理状态信息。

#通过 TCP 访问内网机器:
#1.公网通过ssh访问内部服务器
[ssh]
type = tcp #连接协议
local_ip = 127.0.0.1 #内网服务器ip,填你需要转发到的目的ip
local_port = 22 #ssh默认端口
remote_port = 8000 #自定义的访问内部ssh端口号

#2.公网通过ftp(如使用WinSCP)访问内部服务器
[ftp]
type = tcp
local_ip = 127.0.0.1
local_port = 21,10000-10010 #FTP默认端口
remote_port = 2121,10000-10010 #自定义的远程访问端口 使用:打开WinSCP,填写主机IP(VPS的公网IP)、端口(自定义的远程访问端口2121 )、用户名密码(树莓派的用户名和密码) 。也可以通过访问 ftp://服务端IP:2121

#3.公网通过VNC远程访问图像化内部服务器
[range:VNC] #自定义一个配置名称,格式为“[range:名称]”,放在开头
type = tcp
local_ip = 127.0.0.1
local_port = 5900 #vnc默认端口
remote_port = 5900 #远程端口,ssh远程树莓派时使用的端口

#通过 UDP 访问内网机器
[dns]
type = udp
local_ip = 8.8.8.8
local_port = 53
remote_port = 8500

#通过 FRP 客户端代理其它内网机器访问外网
[http_proxy]
type = tcp
remote_port = 9000
plugin = http_proxy

#通过自定义域名访问部署于内网的 Web 服务
[web]
type = http #访问协议
local_ip = 127.0.0.1 #内网服务器ip
local_port = 80 #内网web服务的端口号
custom_domains = www.dengxj.com,dengxj.com #所绑定的公网服务器域名,一级、二级域名都可以,绑定多个域名时用英文“,”分开

详解
通过 TCP 访问内网机器

1
2
3
4
5
6
7
8
9
10
# SSH 通常使用 UDP 协议
[ssh]
type = tcp #连接协议
local_ip = 127.0.0.1 #内网服务器ip
local_port = 22 #ssh默认端口
remote_port = 8000 #自定义的访问内部ssh端口号

#以访问 SSH 服务为例, 修改 FRP 客户端配置文件
#这样就在 FRP 服务端上成功注册了一个端口为 8000 的服务
#可以通过这个端口访问内网机器上 SSH 服务

通过 UDP 访问内网机器

1
2
3
4
5
6
7
8
9
10
# DNS 查询请求通常使用 UDP 协议
# 配置方式和 TCP 基本一致
[dns]
type = udp #连接协议
local_ip = 8.8.8.8 #内网服务器ip
local_port = 53 #ssh默认端口
remote_port = 8500 #自定义的访问内部ssh端口号

# 这里以转发到 Google 的 DNS 查询服务器 8.8.8.8 的 UDP 端口为例。
# 要转发到内网 DNS 服务器只需把 local_ip 改成对应 IP 即可。

通过 FRP 客户端代理其它内网机器访问外网

1
2
3
4
5
6
7
8
9
10
# FRP 客户端内置了 http_proxy 和 socks5 插件
# 通过这两个插件可以使其它内网机器通过 FPR 客户端的的网络访问互联网。
[http_proxy]
type = tcp
remote_port = 9000
plugin = http_proxy

# 其次将需要通过这个代理访问外网的内部机器的代理地址设置为 服务器IP:9000
# 这样就可以通过 FRP 客户端机器的网络访问互联网了
# 如需启用 Socks5 代理,只需将 plugin 的值更换为 socks5 即可

通过自定义域名访问部署于内网的 Web 服务 点此查看实例

1
2
3
4
5
6
7
8
9
10
11
12
13
# 通过自定义域名访问部署于内网的 Web 服务:
[web]
type = http #访问协议
local_ip = 127.0.0.1 #内网服务器ip
local_port = 80 #内网web服务的端口号
custom_domains = 二级域名全称(eg. nwct.dengxj.com)

# 有时需要在公有网络通过域名访问我们在本地环境搭建的 Web 服务
# 但是由于本地环境机器并没有公网 IP,无法将域名直接解析到本地的机器
# 服务端 vhost_http_port 参数 设置 HTTP 访问端口
# 客户端 local_port 参数 设置内网web服务的端口号
# 最后将 ..com 的域名 A 记录解析到 FRP 服务器的公网 IP 上,现在便可以通过 http://.*.com:8080 这个 URL 访问到处于内网机器上对应的 Web 服务。
# :输入 http://nwct.dengxj.com:8080 访问 内网 IP:80 内容

高阶应用

通过代理连接 FRP 服务端

在只能通过代理访问外网的环境内,FRP 客户端支持通过 HTTP_PROXY 参数来配置代理和 FRP 服务端进行通信。要使用此功能可以通过设置系统环境变量 HTTP_PROXY 或者通过在 FRP 客户端 的配置文件中设置 http_proxy 参数来使用此功能。

1
2
3
4
5
[common]
server_addr = your_server_ip
server_port = 7000
protocol = tcp
http_proxy = http://user:pwd@your_server_ip:8080
安全地暴露内网服务(STCP)

对于一些比较敏感的服务如果直接暴露于公网上将会存在安全隐患,FRP 也提供了一种安全的转发方式 STCP。使用 STCP (secret tcp) 类型的代理可以避免让任何人都能访问到穿透到公网的内网服务,要使用 STCP 模式访问者需要单独运行另外一个 FRP 客户端。

下面就以创建一个只有自己能访问到的 SSH 服务代理为例,FRP 服务端和其它的部署步骤相同,主要区别是在 FRP 客户端上。

1
2
3
4
5
6
7
8
9
[common]
server_addr = your_server_ip
server_port = 7000

[secret_ssh]
type = stcp
sk = abcdefg # 只有 sk 一致的用户才能访问到此服务
local_ip = 127.0.0.1
local_port = 22

其次在要访问这个服务的机器上启动另外一个 FRP 客户端,配置如下:

1
2
3
4
5
6
7
8
9
10
11
[common]
server_addr = your_server_ip
server_port = 7000

[secret_ssh_visitor]
type = stcp
role = visitor # STCP 的访问者
server_name = secret_ssh # 要访问的 STCP 代理的名字,和前面定义的[secret_ssh]相同。
sk = abcdefg # 和前面定义的要一致
bind_addr = 127.0.0.1 # 绑定本地端口用于访问 ssh 服务
bind_port = 6005

这样就可以通过本机 6005 端口对内网机器 SSH 服务进行访问。

点对点内网穿透

在传输大量数据时如果都经过服务器中转的话,这样会对服务器端带宽压力比较大。

FRP 提供了一种新的代理类型 XTCP 来解决这个问题,XTCP 模式下可以在传输大量数据时让流量不经过服务器中转。
使用方式同 STCP 类似,需要在传输数据的两端都部署上 FRP 客户端上用于建立直接的连接。

首先在 FRP 服务端 配置上增加一个 UDP 端口用于支持该类型的客户端: bind_udp_port = 7001

1
2
3
4
5
6
7
8
9
10
11
[common]
bind_addr = 0.0.0.0
bind_port = 7000 # 客户端与服务端进行通信的端口,即frp服务端口,需与客户端server_port一致
privilege_token = 123456 # 特权模式密钥,需与客户端frpc.ini一致

vhost_http_port = 8080 # http服务端口,开启后服务端完成通过域名访问部署于内网的 Web 服务部署,这里将 HTTP 访问端口设为 8080
vhost_https_port = 443 # https服务端口

dashboard_port = 7500 # 控制台端口 通过 Dashboard 可以方便的查看 FRP 的状态以及代理统计信息展示 通过 http://[server_addr]:7500 访问 Dashboard 界面,用户名密码默认都为 admin。

bind_udp_port = 7001

其次配置 FRP 客户端,和常规 TCP 转发不同的是这里不需要指定远程端口。
修改前:

1
2
3
4
5
6
7
8
9
[common]
server_addr = your_server_ip
server_port = 7000

[ssh]
type = tcp #连接协议
local_ip = 127.0.0.1 #内网服务器ip
local_port = 22 #ssh默认端口
remote_port = 8000 #自定义的访问内部ssh端口号

修改后:

1
2
3
4
5
6
7
8
9
[common]
server_addr = your_server_ip
server_port = 7000

[p2p_ssh]
type = xtcp
sk = abcdefg # 只有 sk 一致的用户才能访问到此服务
local_ip = 127.0.0.1
local_port = 22

然后在要访问这个服务的机器上启动另外一个 FRP 客户端,配置如下:

1
2
3
4
5
6
7
8
9
10
11
[common]
server_addr = your_server_ip
server_port = 7000

[p2p_ssh_visitor]
type = xtcp
role = visitor # XTCP 的访问者
server_name = p2p_ssh # 要访问的 XTCP 代理的名字
sk = abcdefg
bind_addr = 127.0.0.1 # 绑定本地端口用于访问 ssh 服务
bind_port = 6006

这样就可以通过本机 6006 端口对内网机器 SSH 服务进行访问。

目前 XTCP 模式还处于开发的初级阶段,并不能穿透所有类型的 NAT 设备,所以穿透成功率较低。穿透失败时可以尝试 STCP 的方式。

参考文档
1.FRP内网穿透_内网穿透 无需公网ip
2.使用FRP实现内网穿透,远程访问内网服务器