经历分享,发现挖矿木马后,服务器快速备份与重装(腾讯云平台)

前言

之前我的云服务器就有被入侵挂挖矿木马的情况,当时清理了木马后就没管了。

结果最近又发现了,甚至几台服务器都被挂了挖矿木马,这下不得不来一次大清理,把几个服务器全部重装😂

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 写的,直接下载运行即可。

bash 复制代码
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-linux-amd64
mv coscli-linux-amd64 coscli
chmod +x coscli

运行配置

bash 复制代码
./coscli config init

会进入一个交互式界面,按照提示输入,只输入上面需要准备的几个配置即可,其他的按默认来。

具体配置的解释,可以看官方文档: https://cloud.tencent.com/document/product/436/63144

常用命令

这个 coscli 工具的命令基本和 Linux 文件管理一样,比如:

bash 复制代码
# 列出文件
./coscli ls cos://<bucket-name>/[path]

本文的备份场景里,主要使用 cp 命令,上传本地文件到 COS

bash 复制代码
./coscli cp ./example.txt cos://<bucket-name>/

把两个路径反过来,就是下载

bash 复制代码
./coscli cp cos://<bucket-name>/example.txt ./

创建备份

现在服务器都是用 docker 部署各种应用,同时把 volumes 挂载到本地目录下。

备份主要是打包这部分配置和数据,压缩,上传到 COS

一开始我用的是一行命令解决这个需求

bash 复制代码
# 压缩并显示进度
# 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 二进制文件的路径

bash 复制代码
#!/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 数据库

导出数据使用以下命令:

bash 复制代码
docker exec -t pgsql pg_dumpall -U postgres > pgsql_full_backup.sql

为了方便使用,我还写了个 pgsql-dump 函数,详见下文。

创建硬盘镜像

我的思路是快速恢复线上服务,然后创建服务器的硬盘镜像,在虚拟机里进行分析排查。

腾讯云上的镜像是没法下载的,我选择使用 dd 创建镜像,这种方式比较自由。

一些tricks:

开始之前,先停止所有 docker 容器,然后清理存储空间,大部分占用应该都是 docker

bash 复制代码
docker system prune -a

还有其他可能占用空间的情况,可以参考我之前的文章进行分析: 硬盘空间消失之谜:Linux 服务器存储排查与优化全过程

制作完整硬盘镜像前,先填零,这样压缩出来的镜像会比较小

bash 复制代码
dd if=/dev/zero of=/zero.dat bs=1M status=progress

上面的命令会一直把剩余空间占满,结束后再把文件删除

生成的镜像不能放在当前硬盘(e.g. /dev/vda)下,不然会套娃。

最终我是建立SSH管道,直接传输到本地

powershell 复制代码
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 (-) 是水平分屏)

powershell 复制代码
# 每隔 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 里

bash 复制代码
# 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 中加入:

bash 复制代码
if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

然后把所有的 alias 都塞进 .bash_aliases。这样主配置文件会非常清爽。

函数

一些方便的函数,一样放在 ~/.bashrc 里。

用久了之后发现函数越来越多,这时候就需要思考一种比较合适的集中管理解决方案了,目前我觉得比较可行的是两种:

  • python (typer, cli tools)
  • Justfile/Taskfile

但这是后话,以后再来整理

一键导出 PostgreSQL 备份

bash 复制代码
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?这个函数根据后缀名自动选择解压工具。

bash 复制代码
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
bash 复制代码
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

bash 复制代码
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)。

bash 复制代码
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

bash 复制代码
ftext() {
    # 参数 1: 搜索词,参数 2: 目录 (可选,默认当前)
    grep -rnw "${2:-.}" -e "$1"
}

docker配置

/etc/docker/daemon.json

配置镜像加速器,限制日志大小

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组。

bash 复制代码
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

更新软件包

以上搞定了,总算不是在裸奔了,这时候开始来更新软件。

bash 复制代码
sudo apt update
sudo apt upgrade -y

进阶

以下是一些进阶的安全方案/工具,由于篇幅关系,我就简单列一下,具体配置以后单独写文章介绍。

防火墙 ufw

比起 iptables 来说方便多了

bash 复制代码
# 默认拒绝
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 端口。

bash 复制代码
sudo apt install fail2ban
# 查看状态
sudo fail2ban-client status
Rootkit 检测
bash 复制代码
sudo apt install rkhunter
sudo rkhunter --update
sudo rkhunter --check

sudo apt install chkrootkit
sudo chkrootkit

定期自动安全更新

这是可选的,如果记性好,经常维护服务器,可以手动更新

bash 复制代码
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades

常用工具

监控CPU、网络连接、进程网络等

bash 复制代码
sudo apt install htop iftop nethogs

其他的不列举了,按照各自习惯来吧。

安装docker

部署应用全靠这个,不过现在国内安装有点费劲了,腾讯云服务器可以参考官方文档

我这里直接复制粘贴一份,以下命令依次执行即可。

bash 复制代码
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/

bash 复制代码
sudo usermod -aG docker $USER

杂项

其他的就是把本文上面的 alias, 函数, 工具 等备份好的配置还原到不同配置文件里。

哦对了,这个 Debian 还有个坑,普通用户在 sudo 的时候,环境变量里没有 /usr/sbin(存放 usermod, ip, fdisk 等系统管理命令的目录)默认只包含在 root 用户的 PATH 中。

所以需要手动加入 ~/.profile 里

bash 复制代码
export PATH=$PATH:/usr/sbin:/sbin

小结

就这样吧,这几天折腾服务器搞得心好累。

配置都是很简单的东西,但是操作的时候很繁琐很费时间,以前一直没有一个系统的记录,每次遇到新服务器配置/重装,都要一直问AI或者看文档,效率很低。

本文完整记录这个过程,以后有类似场景可以更好参考,接下来也要慢慢形成更方便好用的工作流,不断提高工作效率。

相关推荐
雯宝4 小时前
mnt_init 函数
linux
律宏阔10 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔10 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴10 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千10 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3710 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3710 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei3710 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW10 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW11 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux