之前讲了通过frp实现内网穿透来实现ssh访问局域网内主机的功能. 事实上frp还可以用来将部署在本地电脑上的服务暴露在公网上, 让用户可以自由地访问. 这让很多老电脑有了变废为宝的机会, 比如我在上面部署了个聊天室. 然而, 有一个问题是如果走tcp或者http协议的话, 配置起来很方便但本地主机到服务器之间的流量是明文的, 会有一些安全隐患. 这篇文章记录一下我配置服务端和客户端之间通信走tls加密的过程.

客户端配置

本文的内容是基于已经配置好了基本的frp的情况的, 所以如果还没开始, 可以先参考这篇[文章].

首先, 官方文档里提供了http和https的传输方式, 可以通过域名来提供不同的服务. 但是我折腾了半天, HTTPS那个一直失败, 所以就选择了官方文档里的另一个方案: 通过tls进行全局加密. 这也是官方推荐的安全配置方式 [Ref].

本文内容是基于官方文档的, 只是官方文档和github主页的简单说明里有些内容有重复和命名混淆, 让我这个小白在里面绕了很多圈子, 这里的讲解会区分它们.

首先, 把openssl的配置文件拷贝过来, 文件路径会有不同, 可以用openssl version -d命令查看.

# 查看配置文件路径
$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"

# 拷贝到frpc本地
cp /usr/lib/ssl/openssl.cnf ./my-openssl.cnf

然后, 在客户端生成ca和给服务端的证书. 记得把IPADDRESS改成服务端的IP.

# 生成ca证书
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt

# 生成给服务端的证书
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:[IPADDRESS],DNS:example.server.com")) \
    -out server.csr

openssl x509 -req -days 3650 -sha256 \
    -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -extfile <(printf "subjectAltName=DNS:localhost,IP:[IPADDRESS],DNS:example.server.com") \
    -out server.crt

然后把server.keyserver.crt拷贝到服务端, 可以用scp命令实现.

服务端配置

同样的, 根据openssl配置文件生成ca证书并给客户端的证书. 这里证书申请里是不用配置IP的, 所以直接复制粘贴运行就好了.

# 查看配置文件路径
$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"

# 拷贝到frps本地
cp /usr/lib/ssl/openssl.cnf ./my-openssl.cnf

# 生成ca证书
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt

# 生成给客户端的证书
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 3650 -sha256 \
    -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \
    -out client.crt

然后把client.keyclient.crt拷贝到客户端, 可以用scp命令实现.

配置文件修改

注意配置文件中关于tls协议的配置比如在[common] section下面.

首先修改客户端的配置文件 frpc.ini.

# 开启tls
tls_enable = true
# 指定ca证书和服务端复制来的client公私钥目录
tls_trusted_ca_file = path/ca.crt
tls_cert_file = path/client.crt
tls_key_file = path/client.key

然后修改服务端的配置文件 frps.ini.

# 强制开启tls, 只接受tls连接的客户端
tls_only = true
# 开启tls
tls_enable = true
# 指定ca证书和客户端来的server公私钥目录
tls_cert_file = path/server.crt
tls_key_file = path/server.key
tls_trusted_ca_file = path/ca.crt

最后, 在客户端上运行./frpc -c frpc.ini, 在服务端上运行./frps -c frps.ini, 查看是否正常运行.

没有问题的话, 就可以systemctl restart它们了. 这样下来, 本地端到服务端之间的通信是全局加密的了.

将本地服务端口暴露给服务端

配置了服务端和客户端之间的流量走tls协议加密之后, 就可以很快地用tcp为本地的服务进行配置. 比如我把本地的rocket chat端口暴露给服务端, 这样服务端通过nginx方向代理就可以像访问服务端的服务一样访问本地的服务了.

[rocketchat_tcp]
type = tcp
local_ip = 127.0.0.1
local_port = 3000
remote_port = 3000
use_compression = true



参考

Ref 1

Ref 2

Ref 3

Tags: none

3 Comments

  1. 作者以非凡的视角解读平凡,让文字焕发出别样的光彩。

  2. 这是一篇佳作,无论是从内容、语言还是结构上,都堪称完美。

  3. 语言通俗易懂,适合目标读者群体。

Leave a comment...