服务器安全审计日志是运维人员的眼睛。没有日志,服务器上发生的一切都是黑盒操作。但手动登录每台机器、复制文件、分析时间戳,这种重复劳动不仅低效,更容易在安全事件发生后的黄金响应窗口期浪费宝贵时间。使用Bash脚本自动化收集日志,不是锦上添花,而是构建基本安全运维能力的必修课。下面直接讲怎么设计一套能落地的自动化收集方案。
确定要收集的核心日志类型动手写脚本前,必须明确目标。安全审计不是把所有文本文件都打包,而是精准抓取能还原攻击链的关键数据。Linux系统中,/var/log目录是主战场。auth.log或secure文件记录了所有用户登录尝试、sudo提权操作、SSH连接细节,这是检测暴力破解和异常登录的第一手证据。syslog或messages文件包含系统级事件、服务重启、内核告警,能反映攻击是否导致服务异常。针对Web服务器,nginx或apache2目录下的access.log和error.log需要重点关注,SQL注入、路径遍历、恶意扫描的痕迹都在里面。history文件容易被忽略,但攻击者执行过的命令往往留在各用户家目录的.bash_history中,收集这些文件能还原入侵后的横向移动路径。还有lastlog、wtmp、btmp这类二进制日志,需要用last和lastb命令解析后再收集,它们记录成功和失败的登录会话。
构建基础收集脚本框架一个生产级脚本需要处理三个核心问题:收集什么、怎么打包、如何传输。先看最基础的版本,用tar命令配合时间戳命名,避免文件名冲突。脚本开头必须声明解释器,并设置严格错误处理,防止单个命令失败后继续执行导致不完整的数据集。
#!/bin/bash
set -euo pipefail
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
HOSTNAME=$(hostname)
COLLECT_DIR="/tmp/security_audit_${HOSTNAME}_${TIMESTAMP}"
mkdir -p "$COLLECT_DIR"
# 复制文本日志
cp /var/log/auth.log "$COLLECT_DIR/" 2>/dev/null || true
cp /var/log/syslog "$COLLECT_DIR/" 2>/dev/null || true
cp /var/log/messages "$COLLECT_DIR/" 2>/dev/null || true
# 收集所有用户的bash历史
for user_home in /home/*; do
user=$(basename "$user_home")
if [ -f "$user_home/.bash_history" ]; then
cp "$user_home/.bash_history" "$COLLECT_DIR/bash_history_${user}"
fi
done
# 解析二进制登录日志
last > "$COLLECT_DIR/last_output.txt" 2>/dev/null || true
lastb > "$COLLECT_DIR/lastb_output.txt" 2>/dev/null || true
# 打包
tar czf "${COLLECT_DIR}.tar.gz" -C /tmp "$(basename "$COLLECT_DIR")"
rm -rf "$COLLECT_DIR"
echo "收集完成: ${COLLECT_DIR}.tar.gz"
这段代码做了几件关键的事。2>/dev/null || true确保日志文件不存在时脚本不会中断,因为不同发行版日志路径可能不同。循环遍历/home目录收集每个用户的命令历史,这在多用户服务器上尤其重要。last和lastb命令的输出直接重定向到文本文件,把二进制数据转成可读格式,方便后续分析。
扩展审计范围到进程和网络状态光有历史日志不够。攻击正在进行时,当前系统状态才是最有价值的情报。脚本应该同时快照进程树、网络连接、计划任务和自启动项。这些信息能揭示持久化后门、反弹Shell、挖矿进程等活跃威胁。
# 进程和网络快照
ps auxf > "$COLLECT_DIR/process_tree.txt"
netstat -tulnp > "$COLLECT_DIR/network_listening.txt" 2>/dev/null || ss -tulnp > "$COLLECT_DIR/network_listening.txt"
lsof -i > "$COLLECT_DIR/open_files_network.txt" 2>/dev/null || true
# 持久化机制检查
crontab -l > "$COLLECT_DIR/user_crontab.txt" 2>/dev/null || true
for cron_dir in /etc/cron.* /var/spool/cron/crontabs; do
if [ -d "$cron_dir" ]; then
ls -laR "$cron_dir" > "$COLLECT_DIR/cron_$(basename $cron_dir).txt"
fi
done
# 检查异常SUID文件
find / -perm -4000 -type f 2>/dev/null > "$COLLECT_DIR/suid_files.txt"
# 最近修改的可疑文件
find /etc /var /tmp -type f -mtime -1 2>/dev/null > "$COLLECT_DIR/recent_modified_files.txt"
netstat和ss命令二选一,优先用ss因为它在现代系统中更快更可靠。SUID文件检查是发现提权后门的关键手段,攻击者经常设置一个带SUID位的bash副本来维持root权限。最近24小时内修改的文件列表能快速定位webshell或植入的恶意脚本。
处理敏感信息脱敏和文件权限收集到的日志包含IP地址、用户名、甚至可能误记密码参数。在集中存储前,脚本应该设置严格的文件权限,并考虑对IP地址进行部分脱敏。这不是为了隐藏攻击者信息,而是防止收集包在传输过程中被截获后泄露内部网络拓扑。
# 设置安全权限
chmod 600 "${COLLECT_DIR}.tar.gz"
chown root:root "${COLLECT_DIR}.tar.gz"
# 可选:对IP地址脱敏,保留前两位用于分析
sed -i 's/\([0-9]\{1,3\}\)\.\([0-9]\{1,3\}\)\.[0-9]\{1,3\}\.[0-9]\{1,3\}/\1.\2.x.x/g' "$COLLECT_DIR"/*.txt 2>/dev/null || true
权限设为600意味着只有root用户能读取这个压缩包,防止低权限用户窃取审计数据。IP脱敏的正则替换把后两段替换为x,既保护了内部地址完整结构,又保留了网络段信息用于分析攻击来源的分布。
自动化传输到中央审计服务器日志留在被入侵的机器上等于没收集。攻击者可能会删除日志掩盖痕迹,所以脚本必须把数据传出去。SCP和rsync是两种可靠选择,配合SSH密钥认证实现免密传输。但要注意,用于传输的SSH密钥权限必须严格限制,且审计服务器只接受追加写入,不允许删除操作。
AUDIT_SERVER="audit-server.internal"
AUDIT_PATH="/data/security_logs/${HOSTNAME}/"
# 使用rsync传输,保留时间戳
rsync -avz --remove-source-files \
-e "ssh -i /root/.ssh/audit_collector_key" \
"${COLLECT_DIR}.tar.gz" \
"audit@${AUDIT_SERVER}:${AUDIT_PATH}"
# 验证传输成功
if [ $? -eq 0 ]; then
logger -t security_audit "日志成功传输到审计服务器"
else
logger -t security_audit "日志传输失败,保留本地副本"
fi
rsync的--remove-source-files参数在传输成功后删除本地文件,避免磁盘空间被历史收集包占满。传输失败时用logger记录系统日志,这样即使脚本静默运行也能追踪问题。审计服务器端应该配置append-only文件系统属性,用chattr +a命令设置目录,防止已收集的日志被篡改或删除。
定时执行与完整性校验把脚本部署到crontab实现自动化。执行频率取决于服务器暴露面和业务风险,互联网边界服务器建议每小时收集一次,内网服务器可以放宽到每6小时。但单纯定时执行不够,还需要对收集包做哈希校验,确保传输过程中数据未被篡改。
# 生成SHA256校验和
sha256sum "${COLLECT_DIR}.tar.gz" > "${COLLECT_DIR}.tar.gz.sha256"
# 一并传输校验文件
rsync -avz "${COLLECT_DIR}.tar.gz.sha256" \
"audit@${AUDIT_SERVER}:${AUDIT_PATH}"
在审计服务器端,可以写一个验证脚本定期检查接收到的压缩包哈希是否匹配。不匹配的包说明传输过程出错或文件被篡改,应立即触发告警。这种机制虽然简单,但构成了最基本的证据链完整性保护。
处理大规模集群的并发收集当服务器数量超过几十台,所有机器同时向审计服务器推送日志会造成网络拥塞和磁盘I/O瓶颈。解决方法是引入随机延迟,让每台机器在定时任务触发后随机等待一段时间再执行收集。另外,审计服务器端应该用inotify或systemd path unit监控新文件到达,自动解压并导入SIEM系统。
# 在crontab中添加随机延迟 # 0 * * * * sleep $((RANDOM % 600)) && /usr/local/bin/collect_audit_logs.sh
这个crontab条目让脚本在每小时的第0分钟触发后,随机等待0到600秒再执行。对于100台服务器,它们的收集操作会分散在10分钟内完成,大幅降低峰值压力。审计服务器端可以用Logstash或自写脚本监控目录,解压后按时间戳和主机名建立Elasticsearch索引,实现集中检索。
应对日志被清除的对抗策略高级攻击者入侵后会第一时间清理日志。脚本的价值在于,如果收集频率足够高,攻击者清除日志的窗口期就极短。更进一步,可以结合auditd的实时日志,把内核级审计事件通过netlink socket实时转发到远程服务器。但Bash脚本层面能做的,是检查日志文件是否被异常清空或删除,并在发现时立即触发告警。
# 检测日志文件是否被截断
for logfile in /var/log/auth.log /var/log/syslog; do
if [ -f "$logfile" ]; then
current_size=$(stat -c%s "$logfile" 2>/dev/null || echo 0)
last_size_file="/var/cache/audit_last_size_$(basename $logfile)"
if [ -f "$last_size_file" ]; then
last_size=$(cat "$last_size_file")
if [ "$current_size" -lt "$last_size" ]; then
logger -t security_audit "警告: $logfile 大小异常减小,可能被清除"
fi
fi
echo "$current_size" > "$last_size_file"
fi
done
这段逻辑记录每个日志文件上次检查时的大小,如果当前大小反而变小,说明日志被截断或轮转后旧文件被删除。虽然正常logrotate也会导致大小变化,但结合时间窗口判断,如果在非轮转时间点出现大小骤降,就高度可疑。
脚本自身的安全加固收集脚本本身也是攻击目标。如果攻击者修改了脚本内容,后续收集就完全不可信。脚本文件应该设置为只读,用chattr +i锁定不可变。同时,脚本的哈希值应该存储在审计服务器端,定期远程比对。
# 锁定脚本文件 chown root:root /usr/local/bin/collect_audit_logs.sh chmod 500 /usr/local/bin/collect_audit_logs.sh chattr +i /usr/local/bin/collect_audit_logs.sh
chattr +i设置不可变属性后,连root用户都无法修改或删除这个文件,除非先取消不可变属性。这增加了攻击者篡改脚本的成本。审计服务器端保存一份脚本的SHA256哈希,定期通过SSH远程执行sha256sum比对,发现不一致立即告警。
日志保留策略与合规性不同行业对日志保留时长有明确要求。PCI DSS要求保留一年,等保要求至少六个月。审计服务器端应该按主机名和日期组织目录结构,配合find命令定期清理过期数据。压缩包在存储前可以再次用xz压缩,节省长期存储空间。
# 审计服务器端清理超过180天的日志 find /data/security_logs/ -type f -name "*.tar.gz" -mtime +180 -delete
这个清理策略应该配置在审计服务器上,而不是客户端脚本中,因为客户端可能被入侵后恶意删除历史数据。集中存储、集中清理、集中备份,才能保证日志的完整生命周期管理。
这套方案的核心思路是把日志收集从被动的事后翻找,变成主动的、高频的、带完整性保护的自动化流程。脚本本身不复杂,但覆盖了收集、脱敏、传输、校验、告警的完整链条。在实际部署中,根据服务器角色调整收集的日志类型,Web服务器加重应用日志权重,数据库服务器增加慢查询和错误日志收集。安全审计的本质是建立不可篡改的事实记录,Bash脚本就是最朴素也最可靠的工具。
