对话机器人 Rasa(十四):Botfront 自带的 MongoDB 端口问题

更新日期: 2023-07-12 阅读次数: 1700 字数: 1142 分类: AI

漏洞现象

在部署好 botfront,并创建了管理员账号之后,第二天发现账号消失了。 我本以为是本地目录权限问题,导致 mongodb 没有将数据写入磁盘。 但是,修改了本地目录权限之后,这个问题又出现了。

感觉出大问题了。

查看 mongodb 日志

通过 docker logs 查看了 mongodb 镜像的日志。

{"t":{"$date":"2023-07-10T12:33:33.424+00:00"},"s":"I",  "c":"-",        "id":20883,   "ctx":"conn158","msg":"Interrupted operation as its client disconnected","attr":{"opId":1499371}}
{"t":{"$date":"2023-07-10T12:33:33.425+00:00"},"s":"I",  "c":"NETWORK",  "id":22944,   "ctx":"conn158","msg":"Connection ended","attr":{"remote":"68.183.135.77:51070","uuid":"b8cbcfc7-e33e-48a8-bcee-cb8b23ecc91c","connectionId":158,"connectionCount":7}}
{"t":{"$date":"2023-07-10T12:33:33.428+00:00"},"s":"I",  "c":"NETWORK",  "id":22944,   "ctx":"conn159","msg":"Connection ended","attr":{"remote":"68.183.135.77:51076","uuid":"1fb1b912-85ab-4105-9464-4ad251578595","connectionId":159,"connectionCount":6}}

从 IP 看是一个美国 digital ocean 机房的机器发起的恶意行为。

因为 botfront 安装的 mongodb 默认开放了外网端口,27017, 且没有设置访问账号密码。 所以很快被黑客扫描到了。于是被清空了所有数据。

对应的现象就是管理员账号消失了。

连接 mongodb

安装 mongodb cli

sudo apt install mongodb-clients

本地验证了一下,确实没有设置访问账号密码:

$ mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017

可以看到一个警告信息:

{"t":{"$date":"2023-07-09T07:06:29.183+00:00"},"s":"W",  "c":"CONTROL",  "id":22120,   "ctx":"initandlisten","msg":"Access control is not enabled for the database. Read and write access to data and configuration is unrestricted","tags":["startupWarnings"]}

解决方法一:不对外开发 27017 端口

修改 botfront 生成的 docker compose 配置文件。

将 mongodb 镜像对应的端口

'27017:27017'

改成

'27017'

这样就可以保证 docker 网络组内可以访问 mongodb,而通过宿主机外网则无法访问。

注意要使新的 compose 配置生效,需要使用

docker compose stop mongo
docker compose up -d mongo

而不是

docker compose start mongo

start 依然使用的是旧配置。

通过

docker container ls

就能看到具体的端口变化:

前: 0.0.0.0:27017->27017/tcp, :::27017->27017/tcp         botfront-mongo
后: 0.0.0.0:32768->27017/tcp, :::32768->27017/tcp         botfront-mongo

然后再通过开发机远程连接服务器上的 mongodb,确实再也访问不了。

但是这个方案有个巨大的缺陷,就是 botfront 每次在命令行中 enable 一个新 project 时, 这个 docker compose 文件会被重新生成,然后再次恢复那个漏洞。

解决方法二:屏蔽端口

根本的解决方案有两个:

  1. 修改 iptables。但是我对这个不熟悉,最好找专业的运维去修改
  2. 修改云服务器 web 管理后台,直接在那个网络规则上修改。例如阿里云、腾讯云的网络端口配置

为何在 UFW 里屏蔽不了 27017 端口

虽然通过 UFW 屏蔽了 27017 端口,但是实际测试发现,端口依然可以访问。

$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
80/tcp                     ALLOW       Anywhere
27017/tcp                  DENY        Anywhere
27017/udp                  DENY        Anywhere
80/tcp (v6)                ALLOW       Anywhere (v6)
27017/tcp (v6)             DENY        Anywhere (v6)
27017/udp (v6)             DENY        Anywhere (v6)

参考

https://askubuntu.com/questions/652556/uncomplicated-firewall-ufw-is-not-blocking-anything-when-using-docker

即 docker 会绕过 ufw 直接修改 iptables 规则。

The problem was using the -p flag on containers.

It turns out that Docker makes changes directly on your iptables, which are not shown with ufw status.

Possible solutions are:

Stop using the -p flag. Use docker linking or docker networks instead.

Bind containers locally so they are not exposed outside your machine:

docker run -p 127.0.0.1:8080:8080 ...

If you insist on using the -p flag, tell docker not to touch your iptables by disabling them in /etc/docker/daemon.json and restarting:

{ "iptables" : false }

I recommend option 1 or 2. Beware that option 3 has side-effects, like containers becoming unable to connect to the internet.

$ sudo iptables -L

Chain DOCKER (3 references)
ACCEPT     tcp  --  anywhere             172.20.0.5           tcp dpt:27017

Chain ufw-user-input (1 references)
DROP       tcp  --  anywhere             anywhere             tcp dpt:27017
DROP       udp  --  anywhere             anywhere             udp dpt:27017

docker compose 文件端口配置,中 “80:80” 与 “80” 的区别

在 Docker Compose 文件中,端口配置语法通常采用 "HOST:CONTAINER" 的格式,其中 "HOST" 指的是主机上的端口,而 "CONTAINER" 指的是容器内部的端口。这个语法意味着 Docker 会将主机上的端口映射到容器内的端口。

  • "80:80" 表示将主机上的 80 端口映射到容器内部的 80 端口。这意味着当您在主机上访问 80 端口时,请求将被 Docker 转发到容器内的 80 端口。这种配置方式是最常见的端口映射方式。
  • "80" 表示将容器内部的 80 端口暴露给主机,但不会将主机上的任何端口映射到容器内部的端口。这意味着您可以在容器内部运行服务,但无法从主机上访问该服务。这种配置方式适用于只需要在容器内部运行服务的场景。

查看合集

📖 对话机器人 Rasa 中文系列教程

微信关注我哦 👍

大象工具微信公众号

我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式

tags: rasa