内网穿透工具——frp的实践

前言

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。

frp的优点

  • 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。
  • 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。
  • 代理组间的负载均衡。
  • 端口复用,多个服务通过同一个服务端端口暴露。
  • 多个原生支持的客户端插件(静态文件查看,HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。
  • 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。
  • 服务端和客户端 UI 页面。

安装

下载

目前可以在 Github 的 Release 页面中下载到最新版本的客户端和服务端二进制文件,所有文件被打包在一个压缩包中。

1669967801775.png

部署

解压缩下载的压缩包,将其中的 frpc 拷贝到内网服务所在的机器上,将 frps 拷贝到具有公网 IP 的机器上,放置在任意目录。
编写配置文件,先通过 ./frps -c ./frps.ini 启动服务端,再通过./frpc -c ./frpc.ini启动客户端。如果需要在后台长期运行,建议结合其他工具使用,例如 systemdsupervisor

如果是 Windows 用户,需要在 cmd 终端中执行命令。

配置

服务端配置文件

以Linux服务器为例,编辑配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[common]
# 默认7000,可以自定义
bind_port =7001
# 自定义token
token = xxxxxx

# frp管理后台端口,请按自己需求更改
dashboard_port = 7002
# frp管理后台用户名和密码,请改成自己的
dashboard_user = admin
dashboard_pwd = admin
enable_prometheus = true

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

服务管理

创建或编辑/etc/systemd/system/frps.service文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
# 服务名称,可自定义
Description = frp server
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
# 启动frps的命令,需修改为您的frps的安装路径
ExecStart = /opt/frp/frps -c /opt/frp/frps.ini

[Install]
WantedBy = multi-user.target

系统命令:

1
2
3
4
5
6
7
8
9
10
# 启动frp
systemctl start frps
# 停止frp
systemctl stop frps
# 重启frp
systemctl restart frps
# 查看frp状态
systemctl status frps
# 开机启动
systemctl enable frps

客户端配置

下载对应平台的客户端和二进制文件,修改配置文件frpc.ini:

1
2
3
4
5
6
7
8
9
[common]
server_addr = x.x.x.x
server_port = 7001

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

local_ip 和 local_port 配置为本地需要暴露到公网的服务地址和端口。remote_port 表示在 frp 服务端监听的端口,访问此端口的流量将会被转发到本地服务对应的端口。

分别启动 frps 和 frpc。

通过 SSH 访问内网机器,假设用户名为 test:

1
ssh test@x.x.x.x -p 6000

frp 会将请求 x.x.x.x:6000 的流量转发到内网机器的 22 端口。

而此时访问服务器IP:dashboard_port 还能看到监控信息。

1669969264963.png

frp的tcp模式相当于你的设备直接向公网暴露了一个tcp端口。任何设备都可以尝试连接这个端口。这里就会有很大的安全风险。因此,可以考虑使用stcp的模式。

进阶使用

stcp

对于某些服务来说如果直接暴露于公网上将会存在安全隐患。
首先client1向服务端注册时携带了一个sk,所有期望连接的设备访问端口时都必须先验证sk。
1670392327929.png
然后,client2注册时也携带了通用的sk,就可以通过本机的端口间接连接client1了。
1670392380127.png

使用 stcp(secret tcp) 类型的代理可以避免让任何人都能访问到要穿透的服务,但是访问者也需要运行另外一个 frpc 客户端。

1. 服务端不变
2. 在需要暴露到内网的机器上部署 frpc,且配置如下:

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

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

3. 在想要访问内网服务的机器上也部署 frpc,且配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[common]
server_addr = x.x.x.x
server_port = 7000

[secret_ssh_visitor]
type = stcp
# stcp 的访问者
role = visitor
# 要访问的 stcp 代理的名字
server_name = secret_ssh
sk = abcdefg
# 绑定本地端口用于访问 SSH 服务
bind_addr = 127.0.0.1
bind_port = 6000

4. 通过 SSH 访问内网机器,假设用户名为 test:

1
ssh test@127.0.0.1 -p 6000

已经验证成功了,连上了家里的服务器。需要注意的是如果frpc配置在openwrt上需要对应配置填写。

frp 提供了一种新的代理类型 xtcp 用于应对在希望传输大量数据且流量不经过服务器的场景。使用方式同 stcp 类似,需要在两边都部署上 frpc 用于建立直接的连接。
后来测试了一下xtcp的模式,虽然日志提示没啥错误,但是依然无法连接,可能还是网络环境的问题吧。配置方式和stcp差不多,唯一的区别就是需要在服务端多加一个端口,客户端type为xtcp。

http_proxy/socks5

客户端插件可以被应用在任意类型的代理中,但是需要插件本身的协议能够支持。可以使用http_proxy和socks5进行代理,客户端的机器上配置代理即可访问内网。

Mac 终端:

1
2
3
alias goproxy='export http_proxy=http://远程IP:远程端口'
alias disproxy='unset http_proxy https_proxy'
alias goproxys5='http_proxy=socks5://xxx:xxx@xxxx:6005'
1
2
3
4
5
6
7
8
9
10
11
12
13
[http_proxy]
type = tcp
remote_port = 6004
plugin = http_proxy
plugin_http_user = abc
plugin_http_passwd = abc

[socket5proxy]
type = socks5
remote_port = 6005
plugin = socks5
plugin_user = abc
plugin_passwd = abc

无论是浏览器访问,还是终端访问HTTP地址都可以,但是ssh无法直接访问,暂未解决。还有个不足的地方,会被端口扫描。

static_file

通过 static_file 插件可以对外提供一个简单的基于 HTTP 的文件访问服务。对于这个功能,可以做成一个软件仓库来使用。

1
2
3
4
5
6
7
8
9
10
[test_static_file]
type = tcp
remote_port = 6000
plugin = static_file
# 要对外暴露的文件目录
plugin_local_path = /tmp/file
# 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径
plugin_strip_prefix = static
plugin_http_user = abc
plugin_http_passwd = abc

通过浏览器访问 来查看位于 /tmp/file 目录下的 http://x.x.x.x:6000/static/ 文件,会要求输入已设置好的用户名和密码。

域名访问

首先设置frps:

1
2
3
4
5
6
# frps.ini
[common]
bind_port = 7000
vhost_http_port = 8080
# 用于身份验证,请自行修改,要保证服务端与客户端一致
token = abcdefgh

然后设置frpc:

1
2
3
4
[web]
type = http
local_port = 80
custom_domains = xx.xx.com

参考

------ 本文结束 ------

版权声明

Medivh's Notes by Medivh is licensed under a Creative Commons BY-NC-ND 4.0 International License.
Medivh创作并维护的Medivh's Notes博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Medivh 博客( http://www.mknight.cn ),版权所有,侵权必究。