本文记载了一次我的 VPS 被攻击的经历。

现象

我的 vps 在使用中感觉很卡,仔细一看,cpu 负载一直在 100%,用 top 查看没有异常进程,但总数明显不正常,应该是相关进程被隐藏了。

处理

由于系统卡得几乎无法操作,也就没法安装杀毒软件或其他工具。正在束手无策之际,突然想起之前有个 glances 的 docker 在运行,打开 glances 的页面,果然发现一个进程占了 70~80% 的 cpu 负荷。

systemctl status PID​,显示了详细信息,其中包含了文件路径,于是去删除了相关文件,再 kill -9 PID 杀掉进程。

又查了 crontab -l​,里面被添加了开机启动(@reboot 开头的行),又相应删了这些文件。

然后又凭感觉连猜带蒙的删了一些相关文件,感觉差不多了,重启。进来后觉得还是卡,但实际 cpu 负荷并不高,可能是心理作用吧,洁癖,还是重装吧。

分析

一直在思考 vps 怎么会被挖矿,平时也没安装乱七八糟的东西,更何况也没怎么跑大型的应用。

然后突然想起,为了监控远程 docker,我开了允许远程访问 docker。具体操作参考1

这样带来很危险的后果,只要被扫描到端口,就可以实现攻击,完全不设防。

攻击示例

简单地举个例子:

1
2
3
4
# 攻击者使用无认证的 2375 端口远程连接 Docker 并修改 /etc/hosts
docker -H tcp://<your-server-ip>:2375 run --rm \
  -v /etc/hosts:/etc/hosts \
  alpine sh -c "echo '1.2.3.4 example.com' >> /etc/hosts"

这样子,还有什么不能改的呢?

整改

有两个方案:

套上 TLS

生成服务器 TLS 证书

1
2
3
4
5
6
7
sudo mkdir -p /etc/docker/certs
sudo openssl genrsa -aes256 -out /etc/docker/certs/ca-key.pem 4096
sudo openssl req -new -x509 -days 365 -key /etc/docker/certs/ca-key.pem -sha256 -out /etc/docker/certs/ca.pem
sudo openssl genrsa -out /etc/docker/certs/server-key.pem 4096
sudo openssl req -subj "/CN=$(hostname)" -new -key /etc/docker/certs/server-key.pem -out /etc/docker/certs/server.csr
sudo openssl x509 -req -days 365 -sha256 -in /etc/docker/certs/server.csr -CA /etc/docker/certs/ca.pem -CAkey /etc/docker/certs/ca-key.pem -CAcreateserial -out /etc/docker/certs/server-cert.pem
sudo chmod 600 /etc/docker/certs/*

修改 /etc/docker/daemon.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "tls": true,
  "tlsverify": true,
  "tlscacert": "/etc/docker/certs/ca.pem",
  "tlscert": "/etc/docker/certs/server-cert.pem",
  "tlskey": "/etc/docker/certs/server-key.pem",
  "hosts": [
    "unix:///var/run/docker.sock",
    "tcp://0.0.0.0:2376"
  ]
}

重启 docker

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

生成客户端证书

1
2
3
4
openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -sha256 -out client.csr
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out client-cert.pem

客户端连接

1
2
3
4
5
docker --tlsverify \
  --tlscacert=/path/to/ca.pem \
  --tlscert=/path/to/client-cert.pem \
  --tlskey=/path/to/client-key.pem \
  -H=tcp://<服务器IP>:2376 info

使用 Docker Socket Proxy

使用 tecnativa/docker-socket-proxy 作为代理,限制 API 权限。

1
2
3
4
5
6
7
8
docker run -d \
  --name docker_proxy \
  -p 2375:2375 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e CONTAINERS=1 \
  -e IMAGES=1 \
  -e SERVICES=0 \
  tecnativa/docker-socket-proxy

验证

1
2
3
4
5
6
7
# 使用 curl 进行 API 测试:
curl http://localhost:2375/containers/json
# 仅返回正在运行的容器列表。

# 尝试删除容器:
curl -X DELETE http://localhost:2375/containers/<container-id>
# 提示被拒绝。

总结

  • 以上两个方案,第二个方法更方便也更安全,也是我最后采用的方案。
  • 对于网上的技术资料方案,即使是正规网站,也要切实理解后再实施。