经历分享,发现挖矿木马后,服务器快速备份与重装(腾讯云平台)
前言
之前我的云服务器就有被入侵挂挖矿木马的情况,当时清理了木马后就没管了。
结果最近又发现了,甚至几台服务器都被挂了挖矿木马,这下不得不来一次大清理,把几个服务器全部重装😂
PS: 只能说在AI时代,大家都越来越依赖AI编程(包括我们也是),网络安全更加值得重视了,接下来我会单独写一篇分享我们的实践经验。
本文记录:
- 在服务器上连接COS
- 创建备份 & 完整硬盘镜像
- 按照我习惯的一些通用的服务器配置
- 新系统配置 & 服务器安全加固
连接COS
腾讯云COS有免费额度,用来临时存储服务器备份还不错。
使用 coscli 工具可以在服务器上管理 COS 文件,官方文档: https://cloud.tencent.com/document/product/436/63144
配置 coscli 需要准备这几个配置:
- 具有COS管理权限的子账号ID,对应 coscli 的 APPID
- 通过子账号创建的 SecretId, SecretKey
- bucket name
- bucket endpoint (e.g. cos.ap-guangzhou.myqcloud.com)
以下是操作步骤:
创建bucket
创建与服务器同个区域的bucket
创建子账号
关联这个策略 QcloudCOSFullAccess
记录子账号的ID
然后创建 API 密钥,这就是等下需要的 SecretId, SecretKey
下载与配置
准备工作完成了,下载 coscli,这个工具是 go 写的,直接下载运行即可。
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-linux-amd64
mv coscli-linux-amd64 coscli
chmod +x coscli
运行配置
./coscli config init
会进入一个交互式界面,按照提示输入,只输入上面需要准备的几个配置即可,其他的按默认来。
具体配置的解释,可以看官方文档: https://cloud.tencent.com/document/product/436/63144
常用命令
这个 coscli 工具的命令基本和 Linux 文件管理一样,比如:
# 列出文件
./coscli ls cos://<bucket-name>/[path]
本文的备份场景里,主要使用 cp 命令,上传本地文件到 COS
./coscli cp ./example.txt cos://<bucket-name>/
把两个路径反过来,就是下载
./coscli cp cos://<bucket-name>/example.txt ./
创建备份
现在服务器都是用 docker 部署各种应用,同时把 volumes 挂载到本地目录下。
备份主要是打包这部分配置和数据,压缩,上传到 COS
一开始我用的是一行命令解决这个需求
# 压缩并显示进度
# tar -cf -: 创建压缩流并输出到标准输出
# pv: 计算大小并显示进度条
# gzip: 进行压缩
tar -cf - $SOURCE_DIRS | pv -s $(du -sb $SOURCE_DIRS | awk '{sum+=$1} END {print sum}') | gzip > $BACKUP_NAME
不过发现有些 docker volumes 的文件是 root 创建的,这个命令会权限不足,无法打包
所以后面我进行了一些改造,形成了下面的一键备份上传脚本。
一键脚本
在第一部分,定义变量,SOURCE_DIRS 是要打包的目录,TARGET_COS 是,COSCLI_PATH 是 coscli 二进制文件的路径
#!/bin/bash
# 1. 定义变量
BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S).tar.gz"
SOURCE_DIRS="apps projects"
TARGET_COS="cos://<bucket_name>"
COSCLI_PATH="$HOME/temp/coscli"
echo "🚀 开始备份目录: $SOURCE_DIRS ..."
# 2. 压缩并显示进度
# tar -cf -: 创建压缩流并输出到标准输出
# pv: 计算大小并显示进度条
# gzip: 进行压缩
# tar -cf - $SOURCE_DIRS | pv -s $(du -sb $SOURCE_DIRS | awk '{sum+=$1} END {print sum}') | gzip > $BACKUP_NAME
# (1) 计算总大小(用于 pv 进度条显示)
SIZE=$(sudo du -sb $SOURCE_DIRS | awk '{sum+=$1} END {print sum}')
# (2) 压缩并显示进度
# 使用 sudo tar 确保能读取所有文件
sudo tar -cf - $SOURCE_DIRS | pv -s $SIZE | gzip > $BACKUP_NAME
# (3) 修改压缩包权限(可选)
# 因为是 sudo 生成的,文件所有者会变成 root,这里可以改回 ubuntu 方便后续管理
sudo chown $USER:$USER $BACKUP_NAME
echo "✅ 压缩完成: $BACKUP_NAME"
# 3. 上传到腾讯云 COS
echo "📤 正在上传到腾讯云 COS..."
$COSCLI_PATH cp $BACKUP_NAME $TARGET_COS
# 4. (可选) 清理本地压缩包
rm $BACKUP_NAME
# echo "🧹 已清理本地临时文件。"
echo "🎉 备份任务已完成!"
gzip 的压缩率还可以,体积减小了 50% 以上
备份数据库
数据库的 volumes 虽然备份了,但因为版本问题可能启动时有一些坑,为了保险起见,单独导出一份数据库备份。
我在 docker 里跑的 PostgreSQL 数据库
导出数据使用以下命令:
docker exec -t pgsql pg_dumpall -U postgres > pgsql_full_backup.sql
为了方便使用,我还写了个 pgsql-dump 函数,详见下文。
创建硬盘镜像
我的思路是快速恢复线上服务,然后创建服务器的硬盘镜像,在虚拟机里进行分析排查。
腾讯云上的镜像是没法下载的,我选择使用 dd 创建镜像,这种方式比较自由。
一些tricks:
开始之前,先停止所有 docker 容器,然后清理存储空间,大部分占用应该都是 docker
docker system prune -a
还有其他可能占用空间的情况,可以参考我之前的文章进行分析: 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
制作完整硬盘镜像前,先填零,这样压缩出来的镜像会比较小
dd if=/dev/zero of=/zero.dat bs=1M status=progress
上面的命令会一直把剩余空间占满,结束后再把文件删除
生成的镜像不能放在当前硬盘(e.g. /dev/vda)下,不然会套娃。
最终我是建立SSH管道,直接传输到本地
ssh user@server "sudo dd if=/dev/vda bs=4M status=none | gzip -c" > server_evidence.img.gz
Windows没法显示进度,于是用 Windows terminal 分屏 (快捷键:默认 Alt + Shift + Plus (+) 是垂直分屏,Alt + Shift + Minus (-) 是水平分屏)
# 每隔 5 秒刷新一次文件大小
while($true) {
Get-Item .\server_evidence.img.gz | Select-Object Name, @{Name="Size(MB)";Expression={$_.Length / 1MB}};
Start-Sleep -Seconds 5
}
实际完成后,这个 Gzip 的压缩率还是不错的,20G的服务器硬盘压缩到6G的镜像文件
PS: 改天写个文章介绍下 tmux 和 Windows terminal
alias 配置
简化命令操作,可以放在 ~/.bashrc 里
# some more ls aliases
alias ll='ls -alFh'
alias la='ls -A'
alias l='ls -CF'
# docker
alias compose='docker compose'
alias dcrestart='docker compose down && docker compose up -d --build && docker compose logs -f'
alias headscale='docker exec -it headscale headscale'
进阶,如果有很多的话,可以单独创建一个文件管理,便于维护。
建议在home目录下创建一个 .bash_aliases 文件,然后在 ~/.bashrc 中加入:
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
然后把所有的 alias 都塞进 .bash_aliases。这样主配置文件会非常清爽。
函数
一些方便的函数,一样放在 ~/.bashrc 里。
用久了之后发现函数越来越多,这时候就需要思考一种比较合适的集中管理解决方案了,目前我觉得比较可行的是两种:
- python (typer, cli tools)
- Justfile/Taskfile
但这是后话,以后再来整理
一键导出 PostgreSQL 备份
pgsql-dump() {
# 参数 1: 用户名 (默认为 postgres)
# 参数 2: 文件名 (默认为带时间戳的文件名)
local user="${1:-postgres}"
local timestamp=$(date +%Y%m%d_%H%M%S)
local file="${2:-pgsql_backup_${timestamp}.sql}"
# 检查容器是否正在运行
if ! docker ps --format '{{.Names}}' | grep -q "^pgsql$"; then
echo "Error: Docker container 'pgsql' is not running."
return 1
fi
echo "Exporting database with user [${user}]..."
# 执行导出
if docker exec -t pgsql pg_dumpall -U "$user" > "$file"; then
echo "Successfully exported database to: ${file}"
else
echo "Error: Database export failed."
return 1
fi
}
智能解压 & 压缩
忘了 tar -zxvf 还是 -jxvf?这个函数根据后缀名自动选择解压工具。
extract() {
if [ -f "$1" ] ; then
case "$1" in
*.tar.bz2) tar xvfj "$1" ;;
*.tar.gz) tar xvzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xvf "$1" ;;
*.tbz2) tar xvfj "$1" ;;
*.tgz) tar xvzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "'$1' cannot be extracted via extract()" ;;
esac
else
echo "'$1' is not a valid file"
fi
}
压缩命令(如 tar)的参数比解压还难记(比如 cvzf、cjvf 等等),这个函数可以根据想要的文件后缀名,自动调用对应的压缩工具。
不再需要去背那些繁琐的参数,只需要告诉它你想要什么格式:
- 压缩成 Gzip:
pack project.tar.gz ./my_folder - 压缩成 Zip:
pack images.zip pic1.jpg pic2.jpg - 压缩成 7z:
pack backup.7z /var/www/html
pack() {
# 用法: pack <文件名.扩展名> <目标文件或目录>
if [ $# -lt 2 ]; then
echo "Usage: pack <archive_name.ext> <target_files_or_dirs>"
echo "Supported extensions: .tar.gz, .tar.bz2, .zip, .tar, .7z"
return 1
fi
local archive="$1"
shift
local targets="$@"
case "$archive" in
*.tar.gz|*.tgz) tar -cvzf "$archive" $targets ;;
*.tar.bz2|*.tbz2) tar -cvjf "$archive" $targets ;;
*.zip) zip -r "$archive" $targets ;;
*.tar) tar -cvf "$archive" $targets ;;
*.7z) 7z a "$archive" $targets ;;
*) echo "Error: Unknown extension for '$archive'" ;;
esac
}
在 Linux 环境下,不同的压缩格式有不同的适用场景。了解它们的差异能更好选择工具:
| 格式 | 优点 | 缺点 | 场景 |
|---|---|---|---|
.tar.gz |
速度极快,Unix/Linux 兼容性最好 | 压缩率中等 | 代码发布、日常备份 |
.tar.bz2 |
压缩率比 gz 高 | 压缩/解压速度较慢 | 对文件体积敏感时 |
.zip |
Windows 兼容性完美 | 不保留 Linux 权限信息 | 跨平台传文件给 Windows 用户 |
.7z |
极高的压缩率 | 内存占用高,需要安装 7zip |
超大文件归档 |
快速备份
改配置之前,经常需要给某个文件随手做一个备份。
用法: backup nginx.conf -> 直接生成 nginx.conf.20260303_1327.bak
backup() {
# 快速备份文件:filename -> filename.20240520.bak
local file="$1"
if [ -f "$file" ] || [ -d "$file" ]; then
cp -r "$file" "${file}.$(date +%Y%m%d_%H%M%S).bak"
echo "Backup created: ${file}.$(date +%Y%m%d_%H%M%S).bak"
else
echo "Error: '$file' not found."
fi
}
快速进入 Docker 容器
不需要再打长长的 docker exec -it ... /bin/bash。这个函数支持自定义 Shell(默认为 bash,如果容器里没有 bash 会尝试 sh)。
dex() {
local container="${1}"
local shell="${2:-bash}"
if [ -z "$container" ]; then
echo "Usage: dex <container_name> [shell]"
return 1
fi
docker exec -it "$container" "$shell" || docker exec -it "$container" sh
}
查找文件并在其中搜索内容
在当前目录下所有文件中搜索指定的字符串。
用法: ftext "API_KEY" ./src
ftext() {
# 参数 1: 搜索词,参数 2: 目录 (可选,默认当前)
grep -rnw "${2:-.}" -e "$1"
}
docker配置
/etc/docker/daemon.json
配置镜像加速器,限制日志大小
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
重装
到这里准备工作基本搞定了,接下来选择一个合适的镜像重装吧,(我推荐 Debian)
之前看到同事还有个骚操作,连接到服务器后,直接用官方镜像 dd 覆盖掉整个硬盘,这样重装出来的系统是纯净的官方系统,不是腾讯云魔改过的,不过我还没测试这种玩法。
PS: 这在 Linux 圈子里通常被称为 “DD 重装”。这是一种绕过云厂商预设镜像,实现“系统安装自由”的高级玩法。
优点
- 彻底纯净: 云厂商提供的镜像通常内置了监控脚本(如
tat_agent、barad_agent)和安全组件。DD 重装后的系统完全是你自己的,没有任何厂家残留。- 突破限制: 有些云厂商不提供某个版本的系统(比如你非要用某个老版本的 Debian 或特定的 Alpine Linux),通过 DD 可以无视后台选项,安装任何你想用的系统。
- 去除“魔改”: 部分厂商会修改系统的内核或软件源,DD 操作可以让你获得 100% 官方原汁原味的体验。
现在的技术圈有很多优秀的开源“一键 DD 脚本”(如秋水逸冰或 Vicer 的脚本),它们会自动帮你处理网卡驱动和网络配置,比手动执行
dd指令要安全得多。
新系统配置
本来因为篇幅关系,我想分开两篇文章介绍的,不过太麻烦了,在这里也简单记录一下吧。
后续有时间再展开说。
安全加固
新系统的配置按照我的习惯第一步是先进行安全加固。
安全这部分其实可以展开详细讲讲,我也是正在边学边实践,本文只能简要记录一下。
创建普通用户
Debian 默认是root用户,所以要先创建一个普通用户,设置密码并加入sudo组。
adduser user
usermod -aG sudo user
这里有个很坑的地方,腾讯云好像魔改了sudo配置,直接把sudo组的权限去掉了😂,导致添加用户到sudo组后还是没权限。
所以得执行 visudo ,把 %sudo ALL=(ALL:ALL) ALL 这一行添加进去,很能理解为啥有人要"DD重装"了。
SSH限制
编辑 /etc/ssh/sshd_config ,主要是做几件事:禁止密码登录、禁止root登录、修改默认端口。
# 随便改个端口
Port 12837
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
重启服务生效 sudo systemctl restart ssh
更新软件包
以上搞定了,总算不是在裸奔了,这时候开始来更新软件。
sudo apt update
sudo apt upgrade -y
进阶
以下是一些进阶的安全方案/工具,由于篇幅关系,我就简单列一下,具体配置以后单独写文章介绍。
防火墙 ufw
比起 iptables 来说方便多了
# 默认拒绝
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 允许端口
sudo ufw allow 2222/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# 启动
sudo ufw enable
# 查看
sudo ufw status
哦对了,腾讯云的管理面板有一层防火墙,那里记得把 ICMP 包屏蔽掉,这样别人无法ping通服务器,可以减少很多扫描。SSH端口可以只放行自己电脑的IP地址,更加安全。
fail2ban
服务器必备安全工具,防暴力破解。
默认配置通常就够用了,它会自动保护 SSH 端口。
sudo apt install fail2ban
# 查看状态
sudo fail2ban-client status
Rootkit 检测
sudo apt install rkhunter
sudo rkhunter --update
sudo rkhunter --check
sudo apt install chkrootkit
sudo chkrootkit
定期自动安全更新
这是可选的,如果记性好,经常维护服务器,可以手动更新
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades
常用工具
监控CPU、网络连接、进程网络等
sudo apt install htop iftop nethogs
其他的不列举了,按照各自习惯来吧。
安装docker
部署应用全靠这个,不过现在国内安装有点费劲了,腾讯云服务器可以参考官方文档
我这里直接复制粘贴一份,以下命令依次执行即可。
sudo apt-get update
sudo apt-get install ca-certificates curl -y
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://mirrors.cloud.tencent.com/docker-ce/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://mirrors.cloud.tencent.com/docker-ce/linux/debian/ \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl start docker
至于加速器什么的,可以参考本文的 docker配置小节
最后装完得把当前用户添加到 docker 组,不要每次都 sudo docker
https://docs.docker.com/engine/install/linux-postinstall/
sudo usermod -aG docker $USER
杂项
其他的就是把本文上面的 alias, 函数, 工具 等备份好的配置还原到不同配置文件里。
哦对了,这个 Debian 还有个坑,普通用户在 sudo 的时候,环境变量里没有 /usr/sbin(存放 usermod, ip, fdisk 等系统管理命令的目录)默认只包含在 root 用户的 PATH 中。
所以需要手动加入 ~/.profile 里
export PATH=$PATH:/usr/sbin:/sbin
小结
就这样吧,这几天折腾服务器搞得心好累。
配置都是很简单的东西,但是操作的时候很繁琐很费时间,以前一直没有一个系统的记录,每次遇到新服务器配置/重装,都要一直问AI或者看文档,效率很低。
本文完整记录这个过程,以后有类似场景可以更好参考,接下来也要慢慢形成更方便好用的工作流,不断提高工作效率。

浙公网安备 33010602011771号