配置漂移检测是保障服务器安全基线长期有效的关键手段。安全加固完成后,运维变更、误操作或入侵后门常会悄悄改动 sysctl 参数、SSH 配置、防火墙规则与账号清单,而单次基线核查无法发现这些静默变化。本文给出配置漂移检测的完整落地方案:定义黄金基线、编写采集脚本、定时比对差异、告警到钉钉或邮件,并配合 Ansible check 模式做变更前预检,适用于 Linux 服务器群的持续合规与变更审计。
适用场景
- 服务器群已完成安全加固,需要防止配置被后续变更悄悄改回
- 等保、ISO 27001 等合规审计要求留存配置基线与变更证据
- 多台机器需要批量核查配置一致性,定位单机偏离项
- 怀疑入侵后门修改了 SSH、防火墙或计划任务,需要快速比对
- 发布上线前后需要预检变更是否触碰安全基线
前置条件
- 一台或多台 Linux 服务器,具备 root 权限
- 已安装 bash、coreutils、openssh-client(用于分发脚本)
- 一个存放基线与报告的目录,如
/var/lib/baseline - 可选:Ansible 2.9+ 用于批量执行与 check 模式
- 已确认可信的“黄金主机”,或手工审校后的基线文件
# 创建基线工作目录
mkdir -p /var/lib/baseline/{baseline,current,diff,report}
chmod 700 /var/lib/baseline
# 如使用 Ansible
dnf install -y ansible-core || apt-get install -y ansible
原理说明
配置漂移检测的本质是状态快照与逐项比对。它不监视文件内容是否被改(那是 AIDE 等完整性工具的职责),而是抽取与安全强相关的配置项,形成结构化快照,再周期性重新抽取并与基线做差异比对。
选用哪些采集项决定了检测的有效性。建议覆盖六类:
- 内核参数:
sysctl -a中与安全相关的键,如 ip_forward、rp_filter、tcp_syncookies。 - 服务配置:sshd_config、nginx.conf 等关键文件的有效配置(建议只取非注释行)。
- 账号与权限:/etc/passwd、/etc/shadow 中 UID 为 0 的账号、/etc/sudoers 变更。
- 防火墙规则:iptables-save 或 nft list ruleset 的规则集哈希。
- 计划任务:crontab、/etc/cron.* 与 systemd timer。
- 监听端口:
ss -tulnp的监听列表,新增监听端口往往是后门信号。
比对时不比对文件时间戳,只看内容,避免因重写同内容文件产生假告警。
操作步骤
1. 编写配置采集脚本
保存为 /usr/local/bin/collect-baseline.sh,输出五段结构化文本:
#!/bin/bash
# 采集安全相关配置快照,输出到标准输出
set -euo pipefail
echo "### KERNEL"
sysctl -a 2>/dev/null | grep -E \
'net.ipv4.(ip_forward|conf.all.rp_filter|tcp_syncookies|icmp_echo_ignore_broadcasts)|kernel.randomize_va_space' | sort
echo "### SSHD"
grep -vE '^\s*#|^\s*$' /etc/ssh/sshd_config 2>/dev/null | sort
echo "### USERS"
awk -F: '$3==0 {print $1":"$3":"$7}' /etc/passwd | sort
echo "### FIREWALL"
( iptables-save 2>/dev/null || nft list ruleset 2>/dev/null ) | sha256sum | awk '{print $1}'
echo "### LISTEN"
ss -tulnp 2>/dev/null | awk 'NR>1 {print $1, $5}' | sort
chmod +x /usr/local/bin/collect-baseline.sh
# 在加固完成、状态可信的机器上生成黄金基线
/usr/local/bin/collect-baseline.sh > /var/lib/baseline/baseline/golden.txt
sha256sum /var/lib/baseline/baseline/golden.txt
2. 编写比对脚本
保存为 /usr/local/bin/check-drift.sh,发现差异时退出码非 0,便于告警系统捕获:
#!/bin/bash
set -uo pipefail
BASE=/var/lib/baseline/baseline/golden.txt
CUR=/var/lib/baseline/current/now.txt
DIFF=/var/lib/baseline/diff/$(date +%F_%H%M).diff
/usr/local/bin/collect-baseline.sh > "$CUR"
if diff -u "$BASE" "$CUR" > "$DIFF"; then
echo "OK: 无配置漂移"
rm -f "$DIFF"
exit 0
else
echo "ALERT: 检测到配置漂移,详情见 $DIFF"
cat "$DIFF"
exit 2
fi
chmod +x /usr/local/bin/check-drift.sh
/usr/local/bin/check-drift.sh; echo "exit=$?"
3. 定时执行并告警
# 每 6 小时检测一次;漂移时把 diff 内容投递到告警通道
cat >> /etc/cron.d/baseline-drift <<'EOF'
0 */6 * * * root /usr/local/bin/check-drift.sh > /var/log/baseline-drift.log 2>&1 || \
curl -s -X POST "https://oapi.example.com/robot/send" \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"配置漂移告警 $(hostname)\"}}"
EOF
4. 补充权限与特权项基线
账号清单之外,SUID/SGID 文件与关键文件权限同样需要纳入漂移检测——攻击者常通过给普通程序加 SUID 位实现本地提权,或改写 sudoers 放行任意命令。把这两项追加进采集脚本:
# 追加到 collect-baseline.sh
echo "### SUID"
find / -xdev -perm -4000 -type f 2>/dev/null | sort
echo "### SUDOERS"
cat /etc/sudoers /etc/sudoers.d/* 2>/dev/null | grep -vE '^\s*#|^\s*$' | sort
echo "### CRON"
( crontab -l 2>/dev/null; cat /etc/cron.d/* 2>/dev/null ) | grep -vE '^\s*#|^\s*$' | sort
echo "### TIMERS"
systemctl list-timers --all --no-pager 2>/dev/null | awk '{print $NF}' | sort
SUID 列表尤其关键:一旦出现新的 SUID 二进制,几乎可以确定存在提权后门或异常安装。采集后务必把 SUID 结果单独做一次人工审校,再写入黄金基线,避免把已存在的风险项当成正常状态冻结。
5. 用 Ansible check 模式做变更前预检
把基线项写成 playbook,发布前用 --check --diff 预览是否会改动安全配置:
# baseline.yml
- hosts: web
become: true
tasks:
- name: 确保 rp_filter 严格模式
ansible.posix.sysctl:
name: net.ipv4.conf.all.rp_filter
value: "1"
sysctl_set: true
reload: true
- name: 禁用 SSH 密码登录
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
notify: reload sshd
# 只做预检,不实际改动
ansible-playbook -i hosts baseline.yml --check --diff
配置验证
- 人为制造漂移:
sysctl -w net.ipv4.ip_forward=1,再执行check-drift.sh,应输出 ALERT 且退出码为 2。 - 确认告警通道:查看
/var/log/baseline-drift.log与告警群是否收到消息。 - 回归验证:把改动还原后重跑,应输出
OK: 无配置漂移且退出码为 0。 - 批量核查:
ansible -i hosts all -m command -a '/usr/local/bin/check-drift.sh',逐机确认基线一致。 - 审计留存:
ls -l /var/lib/baseline/diff/应看到按时间命名的历史差异文件,作为合规证据链。
常见问题(FAQ)
Q1:告警太频繁,很多是无关变更怎么办?
先区分“安全相关”与“噪音”。把易变的采集项(如端口顺序、sysctl 全量键)收敛到白名单,只保留安全强相关键;对确实允许波动的项,在比对脚本中用 grep -v 排除。原则是宁可少采也要准报,否则运维会直接忽略告警。
Q2:防火墙规则哈希变化但内容没变,为什么?
iptables-save 输出的 rule 顺序或计数器会导致哈希漂移。应改用 iptables-save 排序后再哈希,或改为逐条提取规则文本、排序后拼接,避免计数器与顺序造成假告警。
Q3:多台机器基线不同,如何统一管理?
按角色(web / db / gateway)分别建立 golden 基线文件,采集脚本按角色选择对应基线路径。Ansible 可按主机组下发不同基线,并在 check 模式中确认全部达标后再执行实际变更。
Q4:检测到漂移后,如何判断是变更还是入侵?
按“变更记录优先”原则排查:先查是否有发布单、配置管理系统的变更记录与操作审计日志(如 journald、sudo 日志);若无对应变更,再看漂移项的性质——新增 SUID、新增监听端口、sudoers 放行、新增计划任务这四类高危项一旦无变更记录,应按入侵事件启动应急响应,而不是简单还原配置了事。
Q5:基线更新会不会掩盖真实的恶意改动?
会有这个风险。因此更新黄金基线必须走审批:将 golden.txt 置于版本控制或加签保存,每次更新保留旧版本与审批记录,并对比新旧基线的差异。这样即使基线被更新,也能追溯是何时、由谁、因何把某项变化“合法化”,审计时才有证据链。
总结
安全加固不是一次性动作,而是持续状态管理。配置漂移检测通过黄金基线 + 定时比对 + 差异告警三件套,把“加固后是否被改回去”变成可观测的指标。落地时聚焦安全强相关项、避免噪音、留存差异证据,并把它接入 Ansible 变更预检流程,即可在等保审计与入侵排查中都拿到可靠的配置证据链。