📌 背景描述
在日常运维中,发现一台 CentOS 7 生产服务器的系统负载异常,CPU 和内存占用持续偏高。通过 ps 命令排查发现,系统内积压了数以百计的 freshclam 和 freshclam-sleep 进程,部分进程的运行时间(etime)甚至长达 40 到 80 天。
由于服务器承载着等保(网络安全等级保护)合规的防病毒要求,必须彻底解决此问题,并重新构建一套合规、稳定且低资源的定时查杀机制。
🔍 第一阶段:紧急止血与根因分析
1. 紧急清理僵尸进程
首先清理堆积的更新进程,恢复服务器正常运行:
sudo killall freshclam
sudo pkill -f "freshclam-sleep"
2. 排查堆积根源
清理后发现,即使重启了 clamav-freshclam 服务,手动执行更新依然失败,日志报 403 Forbidden 错误。
根本原因有两个:
- 版本 EOL:服务器上使用的是 ClamAV
0.103.11,该版本已于 2025 年 9 月停止支持(EOL),官方 CDN 彻底拦截了旧版本的更新请求。 - 双重触发:系统中存在
/etc/cron.d/clamav-update,每 3 小时触发一次freshclam-sleep,与systemd服务产生冲突,导致网络卡顿时不断重试并堆积。
3. 为什么没有直接升级到新版?
尝试升级至 ClamAV 1.0.9+(COPR/Atomicorp 源),但发现 CentOS 7 的 glibc 版本过低,强行安装新版会导致依赖缺失及运行时报错。
最终决策:采用**“重装 EPEL 兼容版本 + 配置 Microsoft 镜像源”**的方案,绕过官方封锁,恢复病毒库更新。
🛠️ 第二阶段:修复病毒库更新与清理冲突
1. 重新安装 ClamAV 及组件
sudo yum makecache fast
sudo yum install -y clamav clamav-update clamav-lib clamav-filesystem clamd
2. 配置 Microsoft 镜像源绕过封锁
编辑 /etc/freshclam.conf,在文件末尾添加微软提供的私有镜像地址:
sudo tee -a /etc/freshclam.conf << 'EOF'
PrivateMirror https://packages.microsoft.com/clamav/
EOF
手动验证更新是否成功:
sudo rm -f /var/lib/clamav/freshclam.dat
sudo freshclam --verbose
日志显示 Database updated 或 bytecode.cvd updated,且不再出现 403,说明镜像生效。
3. 移除旧的 Cron 任务(根治堆积)
删除导致进程雪崩的旧版定时任务,让 systemd 成为唯一的更新入口:
sudo mv /etc/cron.d/clamav-update /etc/cron.d/clamav-update.bak
sudo systemctl restart clamav-freshclam
🛡️ 第三阶段:等保合规定时扫描配置
为了满足等保对“定期查杀”的要求,同时避免影响生产业务,采用 “每日增量 + 每周全量” 的策略。通过 systemd timer 配合 flock 锁,以及 logrotate 控制磁盘占用。
1. 创建扫描脚本
创建 /usr/local/sbin/clamav-scan.sh,内置防重叠锁,优先使用 clamdscan 降低资源占用:
sudo tee /usr/local/sbin/clamav-scan.sh << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
LOG_DIR="/var/log/clamav"
mkdir -p "$LOG_DIR"
# 根据你的生产环境调整扫描目标
DAILY_TARGETS="/var/www /home /srv"
WEEKLY_TARGETS="/"
EXCLUDES=(--exclude-dir='^/(proc|sys|dev|run)' --exclude-dir='node_modules' --exclude-dir='.git')
MODE="${1:-daily}"
# 使用 flock 加锁,防止任务重叠
LOCK_FILE="/var/run/clamav_scan.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "$(date): Previous scan still running, skipping this '$MODE' run." >> "$LOG_DIR/scan-skip.log"
exit 0
fi
if [ "$MODE" = "weekly" ]; then
TARGETS="$WEEKLY_TARGETS"
LOG_FILE="$LOG_DIR/scan-weekly-$(date +%F).log"
else
TARGETS="$DAILY_TARGETS"
LOG_FILE="$LOG_DIR/scan-daily-$(date +%F).log"
fi
# 优先使用 clamdscan(速度快,占用低),降级为 clamscan
if systemctl is-active --quiet clamd@scan; then
SCANNER="clamdscan --fdpass"
else
SCANNER="clamscan"
fi
$SCANNER -ri --multiscan --log="$LOG_FILE" "${EXCLUDES[@]}" "$TARGETS" || true
if grep -q "FOUND" "$LOG_FILE"; then
if command -v mail &>/dev/null; then
mail -s "[$(hostname)] ClamAV malware found in $MODE scan" root < "$LOG_FILE"
fi
fi
EOF
sudo chmod +x /usr/local/sbin/clamav-scan.sh
2. 配置 Systemd 定时任务
每日增量扫描(凌晨 2:30)
sudo tee /etc/systemd/system/clamav-scan-daily.service << 'EOF'
[Unit]
Description=Daily ClamAV Incremental Scan
After=clamd@scan.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/clamav-scan.sh daily
EOF
sudo tee /etc/systemd/system/clamav-scan-daily.timer << 'EOF'
[Unit]
Description=Run daily ClamAV incremental scan
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
每周全量扫描(周日凌晨 3:00)
sudo tee /etc/systemd/system/clamav-scan-weekly.service << 'EOF'
[Unit]
Description=Weekly ClamAV Full Scan
After=clamd@scan.service
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/clamav-scan.sh weekly
EOF
sudo tee /etc/systemd/system/clamav-scan-weekly.timer << 'EOF'
[Unit]
Description=Run weekly ClamAV full scan
[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
3. 日志轮转(防磁盘爆满)
创建 /etc/logrotate.d/clamav-scan,仅保留最近 4 周的扫描日志:
sudo tee /etc/logrotate.d/clamav-scan << 'EOF'
/var/log/clamav/scan-*.log {
weekly
rotate 4
compress
delaycompress
missingok
notifempty
copytruncate
}
/var/log/clamav/scan-skip.log {
monthly
rotate 3
compress
missingok
notifempty
copytruncate
}
EOF
4. 启用并验证定时器
sudo systemctl daemon-reload
sudo systemctl enable --now clamav-scan-daily.timer
sudo systemctl enable --now clamav-scan-weekly.timer
# 验证状态
systemctl list-timers --all | grep clamav-scan
⚠️ 避坑记录(Troubleshooting)
在配置 systemd 服务时,遇到了 Failed to execute operation: Bad message 报错。通过 systemd-analyze verify <file> 排查,发现以下两个常见坑点:
- 缺失 Section:由于直接复制命令,文件缺少了
[Unit]、[Service]等节声明,必须补全。 - BOM 头问题:如果是通过某些编辑器写入的文件,可能会包含不可见的 BOM 头,使用
sed -i '1s/^\xEF\xBB\xBF//' <file>即可去除。
📊 总结
通过上述配置,服务器实现了:
| 目标 | 实现方式 | 效果 |
|---|---|---|
| 合规性 | 每日增量与每周全量扫描 | 满足等保审计要求,日志保留1个月 |
| 稳定性 | flock 和 systemd oneshot 双重锁 |
彻底杜绝了扫描进程重叠堆积的风险 |
| 低开销 | 用 clamdscan 替代 clamscan + logrotate |
大幅降低 CPU/内存峰值,限制磁盘占用 |