事情的起因是,我想把ctfd的网站服务器和靶机服务器分离开。
现在我租了两台服务器,一台在hk,做ctfd服务器,不需要备案就能提供网页服务,但价格稍贵,带宽开了3M,做网页足够,券后100出头(200-100);另一台在成都,TX云的SA2系列性价比很高,配合新人优惠券就是半价(窃喜),能开比较不错的配置,4核8G+7M带宽500多一点点,优惠券500-250。
那么要安全地调用靶机的api来开启docker题目,并提供动态flag,就需要配置docker的tls认证和frpc的basic auth
整个架构看起来是这样的

1. 配置安全的docker api
参考了这篇文章,在此做备份。
配置证书的目的是为了验证CTFd服务器的身份,避免靶机服务器被扫到端口就能被随意利用。
1.1 服务器配置(靶机服务器)
1.1.1 创建certs文件夹,用来存放CA私钥和公钥
以下操作均为root用户
mkdir -pv /etc/docker/certs
cd /etc/docker/certs
1.1.2 创建密码
需要连续输入两次相同的密码
openssl genrsa -aes256 -out ca-key.pem 4096
1.1.3 依次输入密码、国家、省、市、组织名称等(除了密码外其他的可以直接回车跳过)
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
1.1.4 生成server-key.pem
openssl genrsa -out server-key.pem 4096
1.1.5 生成server.csr(把下面的IP换成你自己服务器外网的IP或者域名)
openssl req -subj "/CN=123.123.123.123" -sha256 -new -key server-key.pem -out server.csr
1.1.6 配置白名单
0.0.0.0表示所有ip都可以连接。(这里需要注意,虽然0.0.0.0可以匹配任意,但是仍需要配置你的外网ip和127.0.0.1,否则客户端会连接不上)
echo subjectAltName = IP:0.0.0.0,IP:123.123.123.123,IP:127.0.0.1 >> extfile.cnf
或者也可以设置成域名
echo subjectAltName = DNS:www.example.com,IP:123.123.123.123,IP:127.0.0.1 >> extfile.cnf
1.1.7 将Docker守护程序密钥的扩展使用属性设置为仅用于服务器身份验证
echo extendedKeyUsage = serverAuth >> extfile.cnf
1.1.8 输入之前设置的密码,生成签名证书
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
1.1.9 生成供客户端发起远程访问时使用的key.pem
openssl genrsa -out key.pem 4096
1.1.10 生成client.csr(和上面server.csr的IP相同)
openssl req -subj "/CN=123.123.123.123" -new -key key.pem -out client.csr
1.1.11 创建扩展配置文件,把密钥设置为客户端身份验证用
echo extendedKeyUsage = clientAuth > extfile-client.cnf
1.1.12 生成cert.pem,输入前面设置的密码,生成签名证书
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf
1.1.13 删除不需要的配置文件和两个证书的签名请求
rm -v client.csr server.csr extfile.cnf extfile-client.cnf
1.1.14 为了防止私钥文件被更改以及被其他用户查看,修改其权限为所有者只读
chmod -v 0400 ca-key.pem key.pem server-key.pem
1.1.15 为了防止公钥文件被更改,修改其权限为只读
chmod -v 0444 ca.pem server-cert.pem cert.pem
1.1.16 修改Docker配置,使Docker守护程序仅接受来自提供CA信任的证书的客户端的连接
拷贝安装包单元文件到/etc,这样就不会因为docker升级而被覆盖
cp /lib/systemd/system/docker.service /etc/systemd/system/docker.service
打开/etc/systemd/system/docker.service
, 找到ExecStart=/usr/bin/dockerd
把ExecStart=/usr/bin/dockerd ...
这一行删掉,改成下面的段落
ExecStart=/usr/bin/dockerd --tlsverify \
--tlscacert=/etc/docker/certs/ca.pem \
--tlscert=/etc/docker/certs/server-cert.pem \
--tlskey=/etc/docker/certs/server-key.pem \
-H tcp://0.0.0.0:2376 \
-H unix:///var/run/docker.sock
1.1.17 重新加载daemon并重启docker
systemctl daemon-reload
systemctl restart docker
1.1.18 打包证书
tar cf *.pem certs.tar
然后通过比如stfp复制到客户端去
1.2 客户端配置(CTFd所在服务器)
仍然是root用户
1.2.1 创建证书目录
cd CTFd
mkdir docker-certs
cd docker-certs
1.2.2 将ca.pem
cert.pem
key.pem
这3个文件拷贝到当前目录
tar xf certs.tar CTFd/docker-certs
1.2.3 使用docker客户端测试(注意修改证书路径)
docker --tlsverify \
--tlscacert=/CTFd/docker-certs/ca.pem \
--tlscert=/CTFd/docker-certs/cert.pem \
--tlskey=/CTFd/docker-certs/key.pem \
-H=<server_ip>:2376 version
1.2.4 修改whale的docker配置页面
本来whale还没支持tls,联系上维护者提了个需求,他半小时就写好push了


frp配置在下一段
务必非常小心保管这些key,它们就跟服务器root密码一样重要(众所周知docker是可以进行真实主机提权的)
2. 配置安全的frp服务
在这次部署的情况中,两台独立的vps都有各自的公网ip,故frps和frpc都在靶机服务器,ctfd服务器不需要frp
如果靶机在内网的话,frps可以放到公网服务器,frpc改一下server_ip就好
2.1 修改CTFd服务器的docker-compose.yml
因为之前照着赵师傅的博客配置加了frp的配置,现在需要删掉frp有关的所有服务和网络,回退到CTFd官方repo的配置
2.2 配置靶机服务器
首先拉取frankli0324/ctfd-whale/ctfd-target/docker-compose.yml
(稍后pr)
frps段里面映射的端口范围可以按需更改
由于我觉得并不需要http子域名,此时是ip+端口直接访问
然后修改frp目录下的frps.ini
建议把token换成一个随机字符串,比如去生成一个UUID
[common]
bind_port = 6490
token = random_token
以及frpc.ini
[common]
token = random_token
server_addr = 172.1.0.20
server_port = 6490
pool_count = 200
admin_addr = 0.0.0.0
admin_port = 7400
admin_user = admin_user
admin_pwd = admin_password
其中admin_port被映射到了9003端口,在ctfd的配置里需要写成这样

然后是frpc的模板,在ctfd里写成和上面提到的一样。注意之后不要手动更改靶机服务器里的frpc.ini,此文件会由ctfd-whale远程修改。

3. 单容器题目测试
此时应该就装好了,跑个测试题目看看



看看效果如何


OK,能用
另外提一下,题目的动态flag由whale生成,不需要手动在challenge的页面里添加flag值,whale会在容器生成时把生成的flag值添加到$FLAG
环境变量,故只要选手通过任何方式拿到$FLAG
的值就可以了,这样能有效避免交换flag作弊。

没有评论
你先离开吧:)