在现代网络环境中,内网穿透服务成为越来越重要的需求。而 frp 作为一款功能强大的开源内网穿透工具,为我们提供了便捷的解决方案。然而,安全始终是我们应该关注的重点。在本篇文章中,记录了如何通过合理的配置和措施来提高 frp 的安全性,以确保内网服务在穿透的同时也得到了充分的保护,尽量降低安全风险。
更新 frpc 和 frps 的版本
应始终使用最新的 frpc 和 frps 的软件版本,新版本通常修复了已知的安全漏洞和问题。
配置强密码
在 frps 的配置文件中,为管理员密码和 token 设置强密码。使用长密码、包含大小写字母、数字和特殊字符的组合,并定期更改密码。
限制访问来源
通过配置 frps 的 bind_addr
,仅允许特定的 IP 地址或 IP 地址范围访问 frps 服务。这样可以限制对服务的访问,并减少潜在的攻击面。
使用 stcp
使用 stcp(secret tcp)
类型的代理可以避免让任何人都能访问到要穿透的服务,但是访问者也需要运行另外一个 frpc 客户端。
frps.ini 内容如下:
1 2
| [common] bind_port = 7000
|
在需要暴露到外网的机器上部署 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 = abcdefg local_ip = 127.0.0.1 local_port = 22
|
在想要访问内网服务的机器上也部署 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
role = visitor
server_name = secret_ssh sk = abcdefg
bind_addr = 127.0.0.1 bind_port = 6000
|
通过 SSH 访问内网机器,假设用户名为 test:
1
| ssh -oPort=6000 test@127.0.0.1
|
加密与压缩
每一个代理都可以选择是否启用加密和压缩的功能。
加密算法采用 aes-128-cfb,压缩算法采用 snappy。
在每一个代理的配置中使用如下参数指定:
1 2 3 4 5 6 7
| [ssh] type = tcp local_port = 22 remote_port = 6000 use_encryption = true use_compression = true
|
通过设置 use_encryption = true
,将 frpc 与 frps 之间的通信内容加密传输,将会有效防止传输内容被截取。
如果传输的报文长度较长,通过设置 use_compression = true
对传输内容进行压缩,可以有效减小 frpc 与 frps 之间的网络流量,加快流量转发速度,但是会额外消耗一些 CPU 资源。
使用 TLS 加密
use_encryption
和 STCP
等功能能有效防止流量内容在通信过程中被盗取,但是无法判断对方的身份是否合法,存在被中间人攻击的风险。为此 frp 支持 frpc 和 frps 之间的流量通过 TLS 协议加密,并且支持客户端或服务端单向验证,双向验证等功能。
当 frps.ini
的 common
中 tls_only = true
时,表示 server 端只接受 TLS 连接的客户端,这也是 frps 验证 frpc 身份的前提条件。如果 frps.ini
的 common
中 tls_trusted_ca_file
内容是有效的话,那么默认就会开启 tls_only = true
。
注意:启用此功能后除 xtcp 且 xtcp 的 protocol 配置为 kcp 外,可以不用再设置 use_encryption 重复加密
从 v0.50.0 开始,tls_enable
的默认值将会为 true,默认开启 TLS 协议加密。
如果 frps 端没有配置证书,则会使用随机生成的证书来加密流量。
默认情况下,frpc 开启 TLS 加密功能,但是不校验 frps 的证书。
启用双向验证
双向验证应该是目前最安全的方案。启用双向验证需要先生成证书,首先建立一个文件夹,将以下配置写入 my-openssl.cnf
文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| [ ca ] default_ca = CA_default [ CA_default ] x509_extensions = usr_cert [ req ] default_bits = 2048 default_md = sha256 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca string_mask = utf8only [ req_distinguished_name ] [ req_attributes ] [ usr_cert ] basicConstraints = CA:FALSE nsComment = "OpenSSL Generated Certificate" subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer [ v3_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = CA:true
|
生成 ca:
1 2
| openssl genrsa -out ca.key 2048 openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt
|
生成服务端证书:
1 2 3 4 5 6 7 8 9 10 11 12
| openssl genrsa -out server.key 2048
openssl req -new -sha256 -key server.key \ -subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=server.com" \ -reqexts SAN \ -config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:你的服务端公网ip,DNS:example.server.com")) \ -out server.csr
openssl x509 -req -days 5000 -sha256 \ -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -extfile <(printf "subjectAltName=DNS:localhost,IP:你的服务端公网ip,DNS:example.server.com") \ -out server.crt
|
将 subjectAltName
中的 IP 地址改为服务端的公网 IP,否则客户端连接时会报如下错误:
[W] [service.go:133] login to server failed: tls: failed to verify certificate: x509: certificate is valid for 127.0.0.1, not x.x.x.x
如果需要绑定域名,将 example.server.com
改为域名。
生成客户端证书:
1 2 3 4 5 6 7 8 9 10 11 12
| openssl genrsa -out client.key 2048
openssl req -new -sha256 -key client.key \ -subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=client.com" \ -reqexts SAN \ -config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \ -out client.csr
openssl x509 -req -days 5000 -sha256 \ -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \ -out client.crt
|
最终的文件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| . ├── ca.crt ├── ca.key ├── ca.srl ├── client.crt ├── client.csr ├── client.key ├── my-openssl.cnf ├── server.crt ├── server.csr └── server.key
1 directory, 8 file
|
将证书上传到服务端并修改配置文件,增加 tls 验证相关配置:
1 2 3 4 5 6 7 8 9 10 11
| [common] tls_cert_file = /to/cert/path/client.crt tls_key_file = /to/key/path/client.key tls_trusted_ca_file = /to/ca/path/ca.crt
[common] tls_cert_file = /to/cert/path/server.crt tls_key_file = /to/key/path/server.key tls_trusted_ca_file = /to/ca/path/ca.crt
|
随后重启服务端和客户端。至此,客户端和服务端的双向验证配置完毕。
启用和检查日志
启用 frps 的日志记录功能,并定期检查日志以检测异常连接或潜在的安全问题。
1 2 3
| log_file = ./frps.log log_level = info log_max_days = 5
|
强化服务器安全
除了保护 frps 服务本身外,还应采取适当的安全措施来保护整个服务器。这包括使用安全的 SSH 配置、禁用不必要的服务、限制用户访问权限等。
总结
- 始终使用最新版本的 frp
- 配置 token 验证,设置复杂密码
- 若无必要,不要开启 dashboard
- 启用双向 tls 验证
- 强化系统安全
参考资料
https://gofrp.org/docs/overview/