frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内网穿透,对外网提供服务,支持 tcp, http, https 等协议类型,并且 web 服务支持根据域名进行路由转发。
相比ngrok,frp内网穿透无需多复杂的配置就可以达到比较好的穿透效果,具有较强的扩展性,支持 tcp, udp, http, https 协议。
在学习搭建前,首先需要理解什么是代理。
Proxy即代理,被广泛应用于计算机领域,主要分为正向代理与反向代理:
正向代理隐藏了真实的请求客户端。服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求,某些科学上网工具扮演的就是典型的正向代理角色。用浏览器访问http://www.google.com时被墙了,于是你可以在国外搭建一台代理服务器,让代理帮我去请求 google,代理把请求返回的相应结构再返回给我。
当多个客户端访问服务器时服务器不知道真正访问自己的客户端是那一台。正向代理中,proxy和client同属一个LAN,对server透明。
反向代理隐藏了真实的服务端,当我们请求 ww.baidu.com 的时候,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道 www.baidu.com 是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。
反向代理中,proxy和server同属一个LAN,对client透明。
FRP实现内网穿透,就在于使用反向代理,使得真实的服务端得以隐藏。当外部用户(client)请求访问服务端VPS的时候,服务端VPS是下图中的客户端(实质上是上图中的server)的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器(下图中的客户端)那里去。这样就实现了内网穿透。
即:
Client = 外部用户
proxy = 公网服务器(需要在服务器上搭建FRP服务端)
Server = 内网服务器(需要在服务器上搭建FRP客户端)
需要额外注意的事项:
注意,除http(s)以外,客户端frpc.ini内任何端口修改时须在以下范围内:
默认端口白名单:2000-3000,3001,3003,4000-50000转发远程桌面时,需先在本机开启允许远程协助 我的电脑-右键属性-远程设置
需要注意frpc所在机器和frps所在机器的时间相差不能超过15分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
服务端(公网服务器)编辑的是:frps.ini
客户端编辑的是:frpc.ini
配置 FRP 服务端的前提条件是需要一台具有公网 IP的设备,得益于 FRP 是 Go 语言开发的,具有良好的跨平台特性。你可以在 Windows、Linux、MacOS、ARM等几乎任何可联网设备上部署。
sudo apt-get update
wget https://github.com/fatedier/frp/releases/download/v0.17.0/frp_0.17.0_linux_amd64.tar.gz
tar -zxvf frp_0.17.0_linux_amd64.tar.gz #解压缩:tar xvf 文件名
cd frp_0.17.0_linux_amd64 #进入解压目录
#修改frps.ini文件
sudo vim ./frps.ini
添加以下内容:
[common]
bind_addr = 0.0.0.0
bind_port = 7000 # 客户端与服务端进行通信的端口,即frp服务端口,需与客户端server_port一致
authentication_timeout = 0 # 如果是使用了国外服务器,需要使用此行关闭时间戳校验
#在frp_0.17.0_linux_amd64目录下,临时启动命令
./frps -c ./frps.ini
#后台保持启动
nohup ./frps -c ./frps.ini &
设置为开机自动启动
sudo vim /etc/systemd/system/frps.service
[Unit]
Description=frps daemon
After=syslog.target network.target
Wants=network.target
[Service]
Type=simple
ExecStart=/root/frp_0.17.0_linux_amd64/frps -c /root/frp_0.17.0_linux_amd64/frps.ini
Restart= always
RestartSec=1min
[Install]
WantedBy=multi-user.target
注意ExecStart中要配置成自己的绝对路径,绝对路径可以通过pwd命令查询。
#启动frps
systemctl daemon-reload
systemctl start frps
#设置为开机启动
systemctl enable frps
sudo apt-get update
wget https://github.com/fatedier/frp/releases/download/v0.17.0/frp_0.17.0_linux_arm.tar.gz
tar -zxvf frp_0.17.0_linux_arm.tar.gz #解压缩:tar xvf 文件名
cd frp_0.17.0_linux_arm #进入解压目录
#修改frpc.ini文件
sudo vim ./frpc.ini
添加以下内容:
[common]
server_addr = your_server_ip #VPS服务器IP
server_port = 7000 #端口,与服务端bind_port一致
#临时启动
./frpc -c ./frpc.ini
#后台保持启动
nohup ./frpc -c ./frpc.ini &
设置为开机自动启动
sudo vim /etc/systemd/system/frpc.service
[Unit]
Description=frpc daemon
After=syslog.target network.target
Wants=network.target
[Service]
Type=simple
ExecStart=/home/pi/frp_0.17.0_linux_arm/frpc -c /home/pi/frp_0.17.0_linux_arm/frpc.ini
Restart= always
RestartSec=1min
[Install]
WantedBy=multi-user.target
#启动frpc
systemctl daemon-reload
systemctl start frpc
#设置为开机启动
systemctl enable frpc
这样就可以在 FRP 服务端上成功建立一个客户端连接,当然现在还并不能对外提供任何内网机器上的服务,因为我们并还没有在 FRP 服务端注册任何内网服务的端口。
[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。
Dashboard 界面:
注意:面板上所有转发的端口都需要在服务器端开放其防火墙!!!
注意:面板上所有转发的端口都需要在服务器端开放其防火墙!!!
注意:面板上所有转发的端口都需要在服务器端开放其防火墙!!!
如开放端口5900
iptables -I INPUT -i eth0 -p tcp --dport 5900 -j ACCEPT
iptables -I OUTPUT -o eth0 -p tcp --sport 5900 -j ACCEPT
[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:名称]”,放在开头
[range:VNC]
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
#所绑定的公网服务器域名,一级、二级域名都可以,绑定多个域名时用英文“,”分开
[common]
默认情况下只要知道 FRP 服务端开放的端口,任意 FRP 客户端都可以随意在服务端上注册端口映射
这样对于在公网上的 FRP 服务来说显然不太安全。FRP 提供了身份验证机制来提高 FRP 服务端的安全性
启用这一特性后,只有 FRP 服务端和客户端的 common 配置中的 privilege_token 参数一致身份验证才会通过
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 服务
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 客户端内置了 http_proxy 和 socks5 插件
通过这两个插件可以使其它内网机器通过 FPR 客户端的的网络访问互联网。
[http_proxy]
type = tcp
remote_port = 9000
plugin = http_proxy
其次将需要通过这个代理访问外网的内部机器的代理地址设置为 服务器IP:9000
这样就可以通过 FRP 客户端机器的网络访问互联网了
如需启用 Socks5 代理,只需将 plugin 的值更换为 socks5 即可
通过自定义域名访问部署于内网的 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 客户端支持通过 HTTP_PROXY 参数来配置代理和 FRP 服务端进行通信。要使用此功能可以通过设置系统环境变量 HTTP_PROXY 或者通过在 FRP 客户端 的配置文件中设置 http_proxy 参数来使用此功能。
[common]
server_addr = your_server_ip
server_port = 7000
protocol = tcp
http_proxy = http://user:pwd@your_server_ip:8080
对于一些比较敏感的服务如果直接暴露于公网上将会存在安全隐患,FRP 也提供了一种安全的转发方式 STCP。使用 STCP (secret tcp) 类型的代理可以避免让任何人都能访问到穿透到公网的内网服务,要使用 STCP 模式访问者需要单独运行另外一个 FRP 客户端。
下面就以创建一个只有自己能访问到的 SSH 服务代理为例,FRP 服务端和其它的部署步骤相同,主要区别是在 FRP 客户端上。
[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 客户端,配置如下:
[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
[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 转发不同的是这里不需要指定远程端口。
修改前:
[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端口号
修改后:
[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 客户端,配置如下:
[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: https://blog.csdn.net/u012636124/article/details/86618424
2: https://blog.csdn.net/u012636124/article/details/86770279
3: https://www.jianshu.com/p/00c79df1aaf0