背景
为什么某些情况下 frp 会被防火墙拦截,有没有办法防止 frp 被防火墙拦截呢?如何实现登录的安全性?
协议
首先需要了解 frp 的协议层级,感兴趣的可以移植我之前的文章 frp 的协议详解。
防火墙有基于 ip 限制的,有基于端口限制的,有基于协议限制的,也有基于一些报文内容的。基于报文内容的限制,在 frp 这里可以通过一些配置避免掉,比如报文加密。
在 frp 这里,如果是纯 TCP 连接或者纯 KCP 连接,登录是不带加密属性的,也就是明文的。所以很容易被防火墙识别到,但是如果开启了 TLS,则通信过程中所有的数据全程加密。
实践
测试环境
macOS 12.0.1 frp v0.38.0 go1.17
TCP
测试 frp 的默认模式,纯 TCP 的连接。配置信息如下。
# frps
[common]
bind_addr = 0.0.0.0
bind_port = 7003
bind_udp_port = 7001
kcp_bind_port = 7003
dashboard_addr = 0.0.0.0
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = admin
enable_prometheus = true
tls_only = false
# frpc
[common]
server_addr = 127.0.0.1
server_port = 7003
log_level = trace
tcp_mux = true
login_fail_exit = true
protocol = tcp
tls_enable = false
[ssh]
type = tcp
local_ip = 0.0.0.0
local_port = 22
remote_port = 6000
启动 frps 和 frpc,并且启动 wireshark 抓包 frps 的端口。会得到如下的抓包数据。
- client 端发送到 server 端的请求数据。
- server 端返回给 client 端的数据。
可以看到,当只是使用裸的 TCP 协议的时候,client 端发送的数据和 server 端返回的数据是明文显示的,抓包可以清楚的看到。
KCP
接下来我们来看下纯的 KCP 协议。 KCP 是基于 udp 协议实现的类 TCP 协议。KCP 在协议层面保证了数据不丢失,报文重传等 TCP 特性。更多的细节可以详细看下
https://www.improbable.io/blog/KCP-a-new-low-latency-secure-network-stack
。
配置文件如下。
# frps
同上
# frpc
[common]
同上
protocol = kcp
tls_enable = false
[ssh]
type = tcp
local_ip = 0.0.0.0
local_port = 22
remote_port = 6000
启动 frps 和 frpc,并且启动 wireshark 抓包 frps 的端口。会得到如下的抓包数据。
可以看到,即便是用了 KCP,报文依旧是明文的,完全没有加密。
TCP + TLS
接下来开启 TLS 通信。
# frps
同上
# frpc
[common]
同上
protocol = tcp
tls_enable = true
[ssh]
type = tcp
local_ip = 0.0.0.0
local_port = 22
remote_port = 6000
启动 frps 和 frpc,并且启动 wireshark 抓包 frps 的端口。会得到如下的抓包数据。
从报文里看到,frpc 和 frps 完成 TCP 的 3 次握手之后,开始了 TLS 的握手协议,TLS 协议完成握手之后,才开始数据传输。我们大概可以判断报文 67 和 68 应该是客户端发送给服务端的数据和服务端返回给客户端的数据。点进去发现都是被加密过的乱码,完全无法识别报文内容。
也就是说开启了 TLS 之后登录协议也被加密了,大大增强了通信的安全性。
KCP + TLS
接下来测试一下 KCP 开启 TLS 之后我们抓到的数据情况。
具体配置如下。
# frps
同上
# frpc
[common]
同上
protocol = kcp
tls_enable = true
[ssh]
type = tcp
local_ip = 0.0.0.0
local_port = 22
remote_port = 6000
启动 frps 和 frpc,并且启动 wireshark 抓包 frps 的端口。会得到如下的抓包数据。
从抓包内容来看,报文是完全加密的,根本无法识别出来。TLS 在 KCP 协议上完成了握手,然后开始了加密通信。
总结
通过上面的实验,我们总结一下,如果要保证通信的安全性,最好开启 TLS。