背景#
在 10 号晚上 11 点的时候群内有相当一部分人都发现后台需要重新初始化了,而 Mongo 数据库被清空,只留下一封勒索信。有先发案例,其他成员,包括我也迅速查了一下,果然我也跟着中枪了,为此 Innei 还专门发了个安全预警。
勒索信原始文本:(From wibus
We delete all databases, but download a copy to our server. The only way of recovery is you must send 0.01 BTC to bc1qmaacz9fdvnkujqlf8m547mzzh0l5t0ajn699th. You have until 48 hours to pay or data will be inaccessible. Once paid please email incomings99112@onionmail.com with code: xxxxxx and we will recover your database. please read https://paste.sh/UY6_vtGL#THGqRdL9oQqUc-28RPDOWSbB for more information.
当然 mx-space 作为一个优秀的 CMS 系统,自带了自动备份,每天凌晨 1 点自动备份当日数据。所以我直接进文件管理把当天的备份文件,还有附近几日的落鸽下载下来,一方面方便恢复,另一方面分享落鸽到群里,看群里有啥说法。
分析#
毫无疑问,既然被黑那证明有漏洞,毕竟俗话说苍蝇不叮无缝的蛋。就群里的几位被黑的用户(包括我)的描述而言被删库的几个都有共同点:没有服务商 / VPS 的防火墙,Mongo 容器开启映射端口且默认直接映射到 0.0.0.0(即公网可直接访问),数据库也无鉴权机制,所以被 gank 了。
解决方案与后期措施#
关闭不必要的映射#
在最开始的安全预警中 Innei 提到了一点,就是关闭不必要的映射。
像之前版本的 Docker Compose 文件中 mongo 和 Redis 各有一个端口映射,由于不指定 IP,Docker 的默认行为会直接将其映射到 0.0.0.0,即暴露到公网。而实际上考虑到 mongo 和 Redis 和主容器在一个网络下,连接没有问题,所以完全可以直接丢掉端口映射。如下是丢掉端口映射后的部分内容。
mongo:
container_name: mongo
image: mongo
volumes:
- ./data/db:/data/db
networks:
- app-network
restart: always
redis:
image: redis
container_name: redis
networks:
- app-network
restart: always
networks:
app-network:
driver: bridge
上防火墙#
其次,考虑到这是端口暴露产生的问题,所以也给服务器安装了个 UFW 拦个端口。当然,安装完后需要给服务器 SSH 端口和 1Panel 端口放行后再启动防火墙,不然连不了服务器 / 上不了面板就尴尬了(
sudo apt update
sudo apt install ufw
sudo ufw allow 22/tcp # 如果 SSH 运行在非标准端口,需要将上述命令中的 22 端口替换为对应的 SSH 端口。
sudo ufw allow 8090/tcp # 放开 1Panel 系统端口,把 8090 换为面板的端口
sudo ufw enable # UFW,启动!
1Panel 也有对应的防火墙功能,在启动 UFW 之后就可以直接在面板管理 UFW 了,非常适合摆烂方便管理。
降低容器权限#
感谢屑 timo 的建议与指导。操作很简单,但我 Linux 了解的不够深入,折腾的时候百度了 114514 回命令(
首先直接改 Compose 配置文件,加一行 user: node
,如下(文件节选)
version: '3.8'
services:
app:
container_name: mx-server
image: innei/mx-server:latest
command: bash ./docker-run.sh
environment:
- TZ=Asia/Shanghai
- NODE_ENV=production
- ALLOWED_ORIGINS
- JWT_SECRET
- ENCRYPT_KEY
- ENCRYPT_ENABLE
volumes:
- ./data/mx-space:/root/.mx-space
ports:
- '2333:2333'
depends_on:
- mongo
- redis
links:
- mongo
- redis
networks:
- app-network
restart: always
user: node
healthcheck:
test:
[
'CMD',
'curl',
'-f',
'http://127.0.0.1:2333/api/v2/ping'
]
interval: 1m30s
timeout: 30s
retries: 5
start_period: 30s
加了这条约束之后,容器内会指定以 node 用户(UID 为 1000)运行命令,而宿主机的进程归属也是用的 UID 1000(有可能系统没有 UID 为 1000 的用户),不再是默认的 root 了,安全风险也小了许多。
但是,容器以 UID 1000 运行了,容器内数据被扔在了 /home/node/
下,而数据文件夹的归属还在 root 手上,所以还得同时修改容器配置文件中的容器内数据位置以及宿主机中对应映射文件夹的归属。而这些都是 Linux 的基本操作了,不想命令行也可以直接在面板的文件管理中改,就不再额外赘述了。