步骤/目录:
1.准备工作
2.编写frpc.ini和compose模板
3.运行并验证
附录:
1.让frps的dashboard使用https
    (1)tls介绍
    (2)生成证书并运行
    (3)访问的注意事项
2.frp双向验证
    (1)介绍
    (2)准备工作
    (3)修改配置并运行

本文首发于个人博客https://lisper517.top/index.php/archives/21/,转载请注明出处。
本文的目的是使用docker、docker-compose在云服务器上运行frps、在树莓派上运行frpc。
本文实验日期为2022年7月27日。本文使用的是树莓派4B(内存8G版),系统为Pi OS 64位桌面版(2022年4月4日更新,镜像名 2022-04-04-raspios-bullseye-arm64.img )。

之前的文章中曾介绍过frp,详见 在树莓派上设置frp穿透 。简单来说,frp的运行需要一台有公网ip的机器和另一台机器(比如没有公网ip的内网机器);frp的作用是把另一台机器的端口映射到公网机器,使得从公网机器也能访问到另一台机器的端口(即内网穿透)。

在docker官方仓库网页可以搜索到很多frp镜像,这里frpc选用的是snowdreamtech/frpc镜像,其下载量为10M+,两个月前更新。frps镜像也是该作者的。

1.准备工作

首先准备云服务器。笔者这里使用的是阿里云,镜像为Docker 19.03。
在云服务器上进行如下操作:

docker pull snowdreamtech/frps
mkdir -p /docker/frps/conf
mkdir /docker/frps/ssl_certificate #存放tls相关证书
#安装docker-compose。使用env命令发现PATH里没有/usr/local/bin,就放在/usr/bin里了
curl -L https://github.com/docker/compose/releases/download/v1.29.2/docker-compose-`uname -s`-`uname -m` > /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose
docker-compose -v
#编辑frps配置
vim /docker/frps/conf/frps.ini

笔者使用的初始frps配置如下:

[common]
bind_port = 7000
#frpc上也要有相同的token值
token = 你的密码
#frps统计板
dashboard_port = 7500
dashboard_user = wuhuqifei
dashboard_pwd = wuhuqifei

#kcp据说是多消耗15%流量、降低tcp延迟。端口可和bind_port相同
kcp_bind_port = 7000

使用初始配置时的运行命令:

docker run -d --name frps -v /docker/frps/conf/frps.ini:/etc/frp/frps.ini --network host --restart=always snowdreamtech/frps

为了让dashboard使用tls协议加密,笔者将frps.ini修改为:

[common]
bind_port = 7000
#frpc上也要有相同的token值
token = 你的密码
#frps统计板
dashboard_port = 7500
dashboard_user = wuhuqifei
dashboard_pwd = wuhuqifei

#kcp据说是多消耗15%流量、降低tcp延迟。端口可和bind_port相同
kcp_bind_port = 7000

#dashboard使用tls的相关配置
dashboard_tls_mode = true
dashboard_tls_cert_file = /ssl_certificate/server.crt
dashboard_tls_key_file = /ssl_certificate/server.key

关于.crt、.key、ca.crt文件的介绍与自己生成的方法见 附录1. ,或者参考 gofrp文档
然后更改命令,加上挂载证书存放目录的部分:

docker run -d --name frps -v /docker/frps/conf/frps.ini:/etc/frp/frps.ini -v /docker/frps/ssl_certificate:/ssl_certificate --network host --restart=always snowdreamtech/frps

记得把.crt(这里不是指ca.crt)、.key文件放到云服务器的/docker/frps/ssl_certificate目录,至此云服务器设置完毕。

接下来对树莓派进行操作。在树莓派上准备目录,拉取镜像:

mkdir -p /docker/frpc/conf
docker pull snowdreamtech/frpc

2.编写frpc.ini和compose模板

nano /docker/frpc/conf/frpc.ini

写入以下内容:

[common]
server_addr = 云服务器ip地址
server_port = 7000
token = 你的密码
login_fail_exit = false


[pi4B-ssh-1]
type = tcp
local_ip = localhost
local_port = 22
remote_port = 50022

其中pi4B-ssh-1这一项映射是为了之后验证。另外由于这项映射要使用到服务器的50022端口,还需要登录到云服务器主页、在防火墙设置里打开该端口(勿用ufw或iptables打开)。
再为树莓派编写compose模板:

nano /docker/frpc/docker-compose.yml

写入以下内容:

version: "3.9"

services:
  frpc:
    image: snowdreamtech/frpc
    network_mode: host
    volumes:
      - /docker/frpc/conf/frpc.ini:/etc/frp/frpc.ini
    logging: 
      driver: syslog
    restart: always

3.运行并验证

cd /docker/frpc
docker-compose config
docker-compose up
#输出结果
frpc_1  | 2022/07/27 03:23:33 [I] [service.go:301] [50829e63df489f3f] login to server success, get run id [50829e63df489f3f], server udp port [0]
frpc_1  | 2022/07/27 03:23:33 [I] [proxy_manager.go:144] [50829e63df489f3f] proxy added: [pi4B-ssh-1]
frpc_1  | 2022/07/27 03:23:33 [I] [control.go:180] [50829e63df489f3f] [pi4B-ssh-1] start proxy success

这时可以登录到frps的dashboard(云服务器ip:7500/),输入设置的用户名密码,在 Proxies-TCP 中,可看到一项名为 pi4B-ssh-1 的服务,说明成功设置了frp。想进一步实验,可以试着用putty连接到 云服务器ip:50022 ,即通过公网ip连接到树莓派ssh。再次提醒,记得在云服务器的设置里打开50022端口。

最后,若要后台运行docker-frpc,可使用docker-compose up -d命令。

最后提醒一下,关于ufw的使用。用frp把树莓派端口映射到服务器,或者使用docker把容器端口映射到宿主机树莓派时,即使没有用ufw打开端口,也能从树莓派本地以外访问到这些端口。所以为了安全起见,请谨慎设置端口映射。

另外,关于dashboard开启https的进一步内容,可阅读 附录1.

附录:

1.让frps的dashboard使用https

在云服务器上开启frps的dashboard是挺简单的,但是由于云服务器有公网ip,暴露dashboard端口其实不太安全。笔者之后了解到,frps的dashboard可以通过一些设置来使用tls协议、走https,于是查找了一些资料后配置成功。

(1)tls介绍

SSL/TLS是一种密码通信框架,其中SSL出现较早,TLS出现较晚,TLS可以说是SSL的后续版本。在 Docker树莓派实践——Typecho 一文中,nginx配置其实就出现了SSL/TLS:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

实际上,HTTPS、SMTP服务都可由SSL/TLS承载。SSL/TLS的具体原理就不介绍了。从使用上来说,使用TLS,可以身份验证、信息加密,使得信息传输更加安全,这样访问服务器端口、使用frp或其他软件时就更加保险。

(2)生成证书并运行

这里主要参考了 gofrp文档
笔者使用的阿里云,虽然为自己的域名申请了一个免费证书,但该证书下载下来后,其apache版本的crt文件分为chain和public,nginx版则只有pem文件,而且都没有ca.crt。实际上,dashboard使用的证书最好还是自己生成。
在树莓派上使用如下命令:

mkdir ~/ssl_cert
cd ~/ssl_cert
find / -name "openssl.cnf" #查找openssl.cnf

openssl.cnf是openssl的配置文件,需要拷贝到~/ssl_cert下。在树莓派上找到了/usr/lib/ssl/openssl.cnf和/etc/ssl/openssl.cnf,但内容与 gofrp文档 有些差异。于是笔者直接复制了 gofrp文档 中的配置:

nano ~/ssl_cert/my-openssl.cnf

写入以下内容:

[ 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(CA即Certification Authority,颁发证书的机构):

openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=www.lisper517.top" -days 5000 -out ca.crt

其中可以把www.lisper517.top改成自己的域名,或者也可以乱写。
然后使用以下命令生成证书:

#第一条命令
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:47.98.39.236,DNS:lisper517.top")) \
    -out server.csr
#第三条命令
openssl x509 -req -days 365 \
    -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -extfile <(printf "subjectAltName=DNS:localhost,IP:47.98.39.236,DNS:lisper517.top") \
    -out server.crt

注意把第二、三条命令中的IP:47.98.39.236,DNS:lisper517.top修改一下,IP改成自己云服务器的ip,DNS改成自己的域名。
这些文件里需要的是server.crt和server.key,而ca.crt在 附录2. 中需要用到,并且可以用于关闭浏览器警告。可以使用如下命令压缩并转移:

zip cert.zip ca.crt server.crt server.key
cd ..
mv ssl_cert /home/pi #或者其他用户,只要能用WinSCP登录。如果设置了root可登录,也可以不mv

因为用WinSCP把server.key转移到win10机器上时出现Permisson denied,所以先压缩一下才能转移。之后解压一下,还是通过WinSCP放到云服务器上(笔者放到了/docker/frps/ssl_certificate目录下,ca.crt可以不放到云服务器上)。另外,如果想通过配置使得win10或linux接受自己颁发的证书,还要记得转移一下ca.crt,这个文件用于确认颁发证书的机构。
最后记得删除临时文件夹:

rm -r /home/pi/ssl_cert

至此就生成并转移了需要的三个文件。然后在浏览器访问 https://域名:7500 ,就会发现dashboard已经使用了https。但是由于证书是自己生成的,浏览器会发出警告。关于警告的解决方法见下一节 (3)访问的注意事项

(3)访问的注意事项

对自己生成的证书,浏览器会认为证书颁发机构不可信任。笔者一般是强行访问,但也可以通过设置来添加到可信任的颁发机构。
在linux上,笔者并未实操,但据说将ca.crt放到/usr/local/share/ca-certificates目录下,然后update-ca-certificates即可。
在win10上,可按win+r键,输入mmc并运行,在 文件-添加/删除管理单元 中,在左侧单击 证书 ,点击 添加 ,然后可以为本用户或计算机所有用户添加,点击完成、确定,然后点击 证书 - 当前用户 (或 证书 - 计算机用户) 的下拉菜单,点击 受信任的根证书颁发机构-证书 ,在中间会出现许多颁发机构。在中间空白处右键单击,然后选择 所有任务-导入 ,最后把自己的ca.crt导入即可。最后关闭,将设置存入 控制台1.msc 即可。之后重启电脑,就不会出现安全警告了。

2.frp双向验证

(1)介绍

详见 gofrp文档 。简单来说,frp可以使用use_encryption或STCP等功能让传输过程安全,但无法判断对方身份是否合法。而使用tls、双向验证,不仅能加密,还能验证身份。

(2)准备工作

刚才介绍dashboard使用https时提到过,ca.crt代表了证书颁发机构,如果server.crt和server.key是ca签发或在ca的信任链中,则server.crt和server.key是合法的。也可以理解为,ca.crt为server.crt和server.key提供合法性,只要验证了ca.crt颁发了这两个文件,那么这两个文件就可以信任。
frp双向验证的双向,指的就是frpc和frps对彼此的身份都要验证。在前文中笔者演示了自己颁发证书,生成了ca.crt、server.crt和server.key,这里我们称为server_ca.crt、server.crt和server.key;使用类似的方法生成client_ca.crt、client.crt和client.key,那么frpc端上存放server_ca.crt、client.crt和client.key,其中server_ca.crt用来对frps端进行验证,client.crt和client.key则提供给frps端以使frps端对frpc端进行验证;对称地,frps端上存放client_ca.crt、server.crt和server.key,其中client_ca.crt用来对frpc端进行验证,server.crt和server.key则提供给frpc端以使frpc端对frps端进行验证。
前文已经生成了server_ca.crt、server.crt和server.key,虽然是给dashboard用的,但也能用于双向验证。接下来只演示生成client_ca.crt、client.crt和client.key。之前的步骤见前文,这里只有最后三条命令有改动:

#第一条命令
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:lisper517.top,DNS:www.lisper517.top")) \
    -out client.csr
#第三条命令
openssl x509 -req -days 365 \
    -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -extfile <(printf "subjectAltName=DNS:lisper517.top,DNS:www.lisper517.top") \
    -out client.crt

同样注意把DNS:lisper517.top,DNS:www.lisper517.top改成自己的域名,或者乱写也可以。
同样,转移client.key的时候提示Permisson denied,也用zip压缩后转移。另外,转移ca.crt的时候务必分清,最好是像下文中一样改个名字。
最后,记得删除临时文件夹。

(3)修改配置并运行

vim /docker/frps/conf/frps.ini

frps配置新增四行:

[common]
bind_port = 7000
token = 你的密码
dashboard_port = 7500
dashboard_user = wuhuqifei
dashboard_pwd = wuhuqifei
kcp_bind_port = 7000
dashboard_tls_mode = true
dashboard_tls_cert_file = /ssl_certificate/server.crt
dashboard_tls_key_file = /ssl_certificate/server.key
#新增内容
tls_only = true
tls_cert_file = /ssl_certificate/server.crt
tls_key_file = /ssl_certificate/server.key
tls_trusted_ca_file = /ssl_certificate/client_ca.crt

然后把三个文件放到/docker/frps/ssl_certificate文件夹中。注意这里的ca.crt是生成client.crt、client.key时生成的,即client_ca.crt。
最后重启docker-frps,服务器端配置完毕:

docker restart frps

然后对树莓派进行如下修改:

mkdir /docker/frpc/ssl_certificate
nano /docker/frpc/conf/frpc.ini

同样增加四行内容:

tls_enable = true
tls_cert_file = /ssl_certificate/client.crt
tls_key_file = /ssl_certificate/client.key
tls_trusted_ca_file = /ssl_certificate/server_ca.crt

同样把三个文件放到文件夹中,同样注意这里的ca.crt是生成server.crt、server.key时生成的,即server_ca.crt。
然后挂载/docker/frpc/ssl_certificate:

nano /docker/frpc/docker-compose.yml

volumes下新增:

- /docker/frpc/ssl_certificate:/ssl_certificate

由于新增了挂载,需要删除容器、重新生成:

docker stop frpc_frpc_1
docker container rm frpc_frpc_1
cd /docker/frpc
docker-compose up

查看一下日志,是否成功连接。成功连接后使用-d后台运行:

docker stop frpc_frpc_1
docker-compose up -d

标签: frp, 树莓派, docker, docker-compose, SSL/TLS, HTTPS

添加新评论