记录一下我的服务器被入侵之后,我所做的所有补救措施。在这之前,我得说一句:
有 AI 真的太好了!所有问题都能立刻得到解答,这种极速获取知识的方式真的太适合我这种喜欢提问的人了!
换新的服务器
不确定被入侵的服务器是否还留有后门,所以将数据迁移到了一台新的服务器。
在此过程中,发现轻量应用服务器并不适合对安全性与稳定性有较高要求的服务,所以新的服务器使用的是云服务器。
升级所有版本号:NPM、Docker 镜像,甚至是 GitHub Actions 里用到的 Action
即使不是被应用到生产环境的包(例如 eslint)也全都升级到了最新版。整个过程繁琐又枯燥,没啥技术含量,就不赘述了。
服务器加固:定期升级所有 APT 包
sudo apt update && sudo apt upgrade -y
服务器加固:SSH
修改默认端口号、关闭密码登录等。
服务器加固:UFW 防火墙
仅放通了自定义的 SSH 端口。原本还放通了用于提供 HTTPS 的 443 端口,但是在发现 Docker 发布的端口会绕过 UFW 的限制之后就删了。
服务器加固:优化数据库查看方式
以前的数据库查看方式是:
- 在服务器上临时运行一个连接到数据库的 GUI 应用(例如 mongo-express 这种)
- 在本地电脑打开网址,输入 Basic Auth 的账号登录
但在这个过程中发现一个现象,那就是 Docker 发布的端口会绕过 UFW 的限制。询问了 AI 之后,发现了一个更安全的、不用暴露端口到公网的方式。
- 用 SSH 绑定本地端口到服务器端口:
ssh -L 10801:localhost:10810 prod_server - 在服务器上临时启动数据库 GUI 应用,但是启动时不暴露端口到公网,仅绑定本地,例如:
docker run -p 127.0.0.1:10810:10810 db-gui - 在本地电脑打开
https://localhost:10810
服务器加固:Fail2Ban
防止暴力破解的,虽然我配置了 SSH 仅支持密钥登录,但还是加一个。
服务器加固:AppArmor
也是这次事件才知道 Docker 跟 AppArmor 配合很好。
Ubuntu 服务器会默认启用 AppArmor,但是我用的 Ubuntu 是云厂商的魔改版,默认没有启用 AppArmor,查了下如果要启用的话得修改 GRUB 啥的。我准备换回官方的 Ubuntu 服务器。
Docker 容器加固:限制容器能力
三板斧:
no-new-privileges:true禁止权限提升cap_drop: ALL移除所有 Linux 能力read_only: true文件系统只读,禁止任何写操作
还准备限制网络,待实施。
Docker 容器加固:非 root 权限
Docker 默认以 root 权限运行容器,如果容器内的服务有漏洞导致黑客拿到了 shell,那么他就有了容器内的 root 权限,大概率就能逃逸出容器到宿主机,或者即使无法逃逸,也能在容器内运行病毒来占用宿主机的资源。
所以,改为以非 root 权限运行容器:
# 创建非 root 用户用于运行应用
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 复制工程文件并赋予用户权限
COPY --chown=nodejs:nodejs ./ ./
# 切换到非 root 用户运行应用
USER nodejs
CMD ["node", "./index.js"]
改造进行到这里的时候,我都是在本地测了一下,没问题我就发生产了。但是,这次发生产之后,遇到了 Docker Volume 权限的问题,还好赶紧回滚了,但也因此让我意识到本地环境跟生产哪怕只有一点不一样,可能也会出问题,所以又部署了一台测试用的服务器。
先说说 Docker Volume 权限的问题。
我在生产环境服务器使用 acme.sh 生成 SSL 证书,而 acme.sh 是以 root 权限运行的,所以它生成的 SSL 证书也是仅限 root 用户读取的。
之前,我的 nodejs 容器也是 root 权限运行的,所以这样没有问题,但当我不以 root 权限运行后,在读取 SSL 证书时就报错了。
解决方案就是让 AI 修改了自动化部署脚本,在每次运行 nodejs 容器前,先在宿主机层面修改 Docker Volume 内证书文件的权限为 1001。
这里我还向 AI 继续提问了:宿主机上并没有 nodejs 这个容器内定义的用户,为什么可以将文件权限修改为 nodejs 可读取的?
原来 Linux 内核系统的权限只认数字(UID),只要知道 UID,那么把文件权限改为这个 UID 即可。然后顺便还追问了 Docker 容器内权限与宿主机权限之间的关系,只能说有 AI 真的太方便了,如果我自己查那不知道要查多久。
部署测试环境服务器、让 AI 写各种自动化脚本
因为上面的问题,我单独部署了一台测试环境服务器,使用跟生产环境一模一样的配置,只是提供的 URL 不同,比如生产环境 URL 是 prod.myserver.com,测试环境是 beta.myserver.com
部署过程中发现手动执行命令的情况太多了,于是让 AI 写了几个自动化脚本,很好用:
- 部署本地代码到测试或生产环境的脚本。支持在首次部署时进行对应域名的 SSL 证书的申请
- 将生产环境服务器的数据库备份到本地的脚本。
- 将本地的数据库备份上传并导入到测试环境数据库的脚本。
- 重新加载 SSL 证书的脚本。
- 用 SSH 隧道连接服务器内部数据库 GUI 程序。
最后
一切都在测试环境验证通过,就等找个时机部署到生产环境了。
主要是从漏洞、服务器和 Docker 三个方面进行了安全加固,接下来还要从代码本身进行加固(例如在接收请求前,删掉不在白名单内的属性等),不过前面这些应该足够安全了,这部分就之后再改吧。