虽然我有数个网站自用在跑,但是服务器这块与我工作无关,永远是小白。
近几个月,一直会遭遇挖矿程序入侵,但是那时候还有腾讯云的主机防护体验卡,只是会通知我然后手动点下修复就好。
过年回来发现,体验卡到期,天塌了。老破小服务器已经满载挖矿好几天。
年后正式启用私有部署的 git 托管管理项目代码,这个中毒问题得好好找下。其实是不想给🐕企鹅送钱。
总而言之,目前的状况是博客前端的端口直接暴露在公网上,next.js 有某种特点(没有深究)被入侵利用,然后污染了前端容器。暂时重新拉取镜像重部署,docker-compose.yml的image段同级增加read_only: true,避免入侵下载挖矿程序,以此临时解决。
本文主要记录一下指令长啥样,因为我记不住。
找出最吃 CPU 的进程
top -o %CPU
切到 root
whoami
id
sudo -n true; echo $?
杀他妈的
例如我这个挖矿进程的 PID 是 53075,就使用 sudo kill -STOP 53075
再看看进程
找到真实的可执行文件,将获得一个路径,如果需要清理可以后续进行
查看启动命令行,其实是辅助排查,结果“空”表明不是用户手动命令行启动的
查看进程启动时的环境变量,如果看到比较熟悉的词,在这里已经可以找到是什么应用被入侵了
Note
如果以上不足以找到根源应用,那么深入排查,以下指令结果无法明示,可以发给 GPT 让他辅助解析并进一步指导
查看进程控制组,比如我这里回复0::/system.slice/docker-9cffb5dca4c53cf437c1499d7df3725132406b42b3e4873837ec1487a7aa2b46.scope,那么已经明确是CONTAINER ID 为这串数字的容器被入侵了
查看进程基本信息
查看父进程信息
查看进程的网络连接情况
详细列出进程打开的文件和网络连接
[!NOTE]
以上这些查询输出,如果看不懂排查不了,就直接丢给 GPT 让他指示下一步就好
经过排查,在我的问题环境下,明确了是某个 docker 容器 9cffb5dca4c53cf437c1499d7df3725132406b42b3e4873837ec1487a7aa2b46 引起的,那我这里就直接查下这是个啥就行了。
回复表明
同时此前查找父进程表明
另一个侧面验证是,前端镜像备份现在膨胀到了2GB,与我保存的稳定版备份400MB相去甚远。
那我这里可以确认是博客前端的 next.js 直接暴露的问题,但是我又不会修,也不想更新前端到最新版本(in神重构了,同步更新后端和迁移好麻烦)。
那就使用 read_only 部署就可以了,docker-compose.yml的image段同级增加read_only: true,下面是当前的。
注意到,上面我使用image: shiroi:local是一个本地镜像,而不用拉取,因为这个镜像来自于一个我保存下来的稳定版压缩包。
其实这个操作我做了好多次,但是每次都会忘得查指令,cd 到压缩包所在处->docker 加载镜像压缩包->打个人看得懂的标签。
类似以下。
使用不拉取的 compose 指令部署
sudo kill -STOP <PID>
top -b -n 1 | head -n 15
sudo readlink -f /proc/<PID>/exe
sudo sh -c 'tr “\0“ ” ” < /proc/<PID>/cmdline; echo’
sudo cat /proc/<PID>/environ | tr “\0“ “\n“ | head -n 60
sudo cat /proc/<PID>/cgroup
sudo ps -o pid,ppid,user,cmd -p <PID>
sudo ps -o pid,ppid,user,cmd -p $(ps -o ppid= -p <PID>)
sudo ss -antup | grep -E "pid=<PID>," || true
sudo lsof -nP -p <PID> -i 2>/dev/null | head -n 50
sudo docker inspect 9cffb5dca4c53cf437c1499d7df3725132406b42b3e4873837ec1487a7aa2b46 --format 'ID={{.Id}}
Name={{.Name}}
Image={{.Config.Image}}
Created={{.Created}}
Entrypoint={{json .Config.Entrypoint}}
Cmd={{json .Config.Cmd}}
WorkingDir={{.Config.WorkingDir}}
EnvCount={{len .Config.Env}}
Mounts={{json .Mounts}}
Ports={{json .NetworkSettings.Ports}}'
ID=9cffb5dca4c53cf437c1499d7df3725132406b42b3e4873837ec1487a7aa2b46
Name=/shiroi
Image=ghcr.io/stbanana/shiroi:latest
Created=2026-01-26T07:33:07.293181432Z
......
[lighthouse@VM-20-5-opencloudos ~]$ sudo ps -o pid,ppid,user,cmd -p $(ps -o ppid= -p 53075)
PID PPID USER CMD
2206 1954 root next-server (v
version: '3'
services:
shiro:
container_name: shiroi
image: shiroi:local
volumes:
- ./.env:/app/.env
read_only: true
restart: always
ports:
- 2323:2323
[root@VM-20-5-opencloudos ~]# cd /root/mx-space/Shiroi
[root@VM-20-5-opencloudos Shiroi]# sudo docker load -i shiroi.tar
b0f89ac7b966: Loading layer 148.4MB/148.4MB
d3b1ea8ff6e6: Loading layer 5.39MB/5.39MB
2497eefc5264: Loading layer 3.584kB/3.584kB
769a089b3661: Loading layer 30.95MB/30.95MB
8a9366fbc004: Loading layer 1.536kB/1.536kB
556c774c2fcc: Loading layer 8.075MB/8.075MB
55405dd49280: Loading layer 4.096kB/4.096kB
66b48a972c24: Loading layer 24.75MB/24.75MB
7f1566c30993: Loading layer 24.29MB/24.29MB
911870b37b57: Loading layer 27.33MB/27.33MB
14c1060772f2: Loading layer 6.655MB/6.655MB
7be455708435: Loading layer 12.18MB/12.18MB
6fdc0a038ef1: Loading layer 5.213MB/5.213MB
ba8c6d828d78: Loading layer 2.56kB/2.56kB
5c557125ffd5: Loading layer 160.3kB/160.3kB
77abd848d7fc: Loading layer 110.1kB/110.1kB
e7f72ced6a18: Loading layer 111.2MB/111.2MB
0d71f84d6130: Loading layer 23.76MB/23.76MB
dade50077aed: Loading layer 22.65MB/22.65MB
Loaded image ID: sha256:8c476da1052b3d1e26bdaae875b0ac026b6c26869224732f2deee58a8349911a
[root@VM-20-5-opencloudos Shiroi]# sudo docker tag sha256:8c476da1052b3d1e26bdaae875b0ac026b6c26869224732f2deee58a8349911a shiroi:local
[root@VM-20-5-opencloudos Shiroi]#
docker compose up -d