接手一台运行了数年的CentOS服务器,最头疼的往往不是服务宕机,而是出了问题后找不到原因。日志文件散落在各处,要么被刷爆磁盘,要么被自动删除得一干二净。解决这个问题的核心手段只有两个:把该记的都记下来,把该清的都清干净,同时保证不占满磁盘。

日志审计不是简单地看/var/log/messages,而是要建立一套完整的证据链。谁在什么时间、从哪个IP、执行了什么命令、造成了什么结果,这些信息在安全事件追溯和合规检查中缺一不可。

auditd系统调用审计的实战配置

CentOS自带的auditd是内核级的审计工具,能记录系统调用、文件访问、命令执行等底层事件。安装很简单,直接执行yum install audit -y即可。但默认配置几乎不能用,必须按需定制。

核心配置文件在/etc/audit/auditd.conf,重点关注几个参数:

log_file = /var/log/audit/audit.log
log_format = RAW
log_group = root
priority_boost = 4
flush = INCREMENTAL_ASYNC
freq = 50
num_logs = 5
max_log_file = 8
max_log_file_action = ROTATE
space_left = 75
space_left_action = SYSLOG
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
disk_error_action = SUSPEND

这里max_log_file设为8MB,num_logs保留5个,意味着审计日志最多占用40MB,不会无限制膨胀。space_left设为75,表示磁盘空间剩余75%时开始告警,admin_space_left降到50%时暂停审计,这些都是防止磁盘被写爆的关键设置。

审计规则写在/etc/audit/rules.d/audit.rules中。针对服务器场景,建议配置以下规则:

# 删除已有规则
-D

# 设置缓冲区大小
-b 8192

# 记录所有用户登录和注销
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock -p wa -k logins

# 监控/etc/passwd和/etc/shadow的修改
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity

# 监控sudoers文件变更
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers

# 记录所有命令执行(针对特定用户)
-a always,exit -F arch=b64 -S execve -F uid=root -k root-commands
-a always,exit -F arch=b32 -S execve -F uid=root -k root-commands

# 监控关键系统文件
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
-w /etc/ssh/sshd_config -p wa -k sshd

# 记录系统时间修改
-a always,exit -F arch=b64 -S clock_settime -k time-change

# 使规则生效
-e 2

规则生效后重启auditd服务:service auditd restart。使用ausearch可以查询审计日志,例如查看某个用户的所有命令:ausearch -ua root -k root-commands --start today。

rsyslog集中化日志采集

auditd负责底层审计,rsyslog则负责应用层和服务层的日志采集。CentOS 7默认使用rsyslog,配置文件在/etc/rsyslog.conf,自定义规则放在/etc/rsyslog.d/目录下。

默认配置有个严重问题:几乎所有日志都往/var/log/messages里塞,时间长了文件巨大无比,排查问题如同大海捞针。必须做精细化分流。在/etc/rsyslog.d/下创建分类配置文件:

# /etc/rsyslog.d/10-auth.conf
auth.* /var/log/secure
authpriv.* /var/log/secure

# /etc/rsyslog.d/20-mail.conf
mail.* /var/log/maillog

# /etc/rsyslog.d/30-cron.conf
cron.* /var/log/cron

# /etc/rsyslog.d/40-nginx.conf
if $programname == 'nginx' then /var/log/nginx/access.log
& stop

# /etc/rsyslog.d/50-mysql.conf
if $programname == 'mysqld' then /var/log/mysql/mysql.log
& stop

这里用到了rsyslog的基于属性的过滤语法,可以把不同服务的日志精确分流到独立文件。& stop表示匹配后不再继续处理,避免重复记录。

对于多台服务器的场景,建议配置远程日志转发。在日志服务器上开启TCP/UDP接收模块:

# /etc/rsyslog.conf 取消注释
module(load="imtcp")
input(type="imtcp" port="514")

客户端配置转发规则:

*.* @@192.168.1.100:514

一个@表示UDP,两个@@表示TCP。生产环境建议用TCP,虽然性能略低但不会丢日志。同时配合本地队列避免网络中断时日志丢失:

$ActionQueueFileName queue
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionQueueType LinkedList
$ActionResumeRetryCount -1
*.* @@192.168.1.100:514
logrotate日志轮转的深度定制

日志轮转是防止磁盘爆满的最后一道防线。CentOS默认的logrotate配置在/etc/logrotate.conf,应用级配置在/etc/logrotate.d/目录下。默认配置太保守,很多日志文件会无限制增长。

logrotate的核心参数需要根据实际日志量调整。以nginx日志为例,一个日访问量百万级的站点,access.log一天就能达到几个GB。默认的weekly轮转完全不够用,必须改为daily甚至按大小轮转:

/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 640 nginx nginx
    sharedscripts
    postrotate
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 `cat /var/run/nginx.pid`
        fi
    endscript
}

这里rotate 14表示保留14天的日志,compress开启gzip压缩,delaycompress延迟一天压缩,保证当天的日志还能被直接读取。postrotate中的kill -USR1是通知nginx重新打开日志文件,这个信号处理比直接restart优雅得多。

对于数据库慢查询日志这类增长极快的文件,建议用maxsize参数:

/var/log/mysql/slow.log {
    size 100M
    rotate 5
    compress
    missingok
    notifempty
    postrotate
        /usr/bin/mysqladmin -u root flush-logs
    endscript
}

size 100M表示文件达到100MB立即轮转,不管时间周期。这种方式比固定时间轮转更可靠,能精确控制磁盘占用。

auditd日志的轮转陷阱

auditd有自己的轮转机制,和logrotate是两套独立系统。很多管理员在这里踩坑:用logrotate去轮转/var/log/audit/audit.log,结果auditd仍然持有旧的文件句柄,日志写到了已被删除的文件中,磁盘空间并没有释放。

正确做法是只依赖auditd自身的轮转,在/etc/audit/auditd.conf中配置max_log_file_action=ROTATE,同时确保/var/log/audit/目录有足够空间。如果非要手动清理,必须执行service auditd rotate触发轮转,而不是直接删除文件。

journald的持久化与清理

CentOS 7引入了systemd-journald,它默认将日志存储在内存的/run/log/journal中,重启即丢失。对于需要持久化审计的场景,必须改为磁盘存储。创建目录/var/log/journal并重启systemd-journald:

mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

journald的清理策略在/etc/systemd/journald.conf中配置:

SystemMaxUse=500M
SystemKeepFree=1G
SystemMaxFileSize=50M
MaxRetentionSec=2week

SystemMaxUse限制journal日志总大小不超过500MB,SystemKeepFree保证磁盘至少剩余1GB空间,MaxRetentionSec设置最长保留2周。这几个参数组合使用,能精确控制journal日志的磁盘占用。

日志完整性校验与防篡改

审计日志的价值在于不可抵赖性,如果日志可以被随意修改,审计就失去了意义。除了通过文件权限控制(chmod 640、chown root),还可以用文件完整性监控工具辅助。

一种轻量方案是定期计算日志文件的哈希值并异地存储。写一个简单的cron脚本:

#!/bin/bash
LOG_DIR="/var/log/audit"
BACKUP_DIR="/opt/log-checksums"
mkdir -p $BACKUP_DIR
find $LOG_DIR -type f -name "*.log" -exec sha256sum {} \; > $BACKUP_DIR/checksums-$(date +%Y%m%d).txt

这个脚本每天生成一份审计日志的SHA256校验和,配合远程备份,事后可以验证日志是否被篡改。

更严格的方案是部署远程日志服务器,所有日志实时转发,本地只保留短期数据。rsyslog的转发配合TLS加密,能防止日志在传输过程中被窃听或篡改。配置TLS需要生成证书,在/etc/rsyslog.d/remote.conf中加载:

$DefaultNetstreamDriver gtls
$DefaultNetstreamDriverCAFile /etc/pki/tls/certs/ca.pem
$DefaultNetstreamDriverCertFile /etc/pki/tls/certs/client-cert.pem
$DefaultNetstreamDriverKeyFile /etc/pki/tls/private/client-key.pem
$ActionSendStreamDriverMode 1
$ActionSendStreamDriver gtls
*.* @@(o)log-server.example.com:6514
磁盘空间监控与自动告警

再好的轮转策略也可能在异常流量下失效。必须配合磁盘监控,在空间不足时主动告警。在crontab中加入:

*/10 * * * * /opt/scripts/check-disk.sh

脚本内容:

#!/bin/bash
THRESHOLD=85
CURRENT=$(df /var/log | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $CURRENT -gt $THRESHOLD ]; then
    echo "Log disk usage at ${CURRENT}% on $(hostname)" | mail -s "Disk Alert" admin@example.com
    # 紧急清理旧日志
    find /var/log -name "*.gz" -mtime +7 -delete
fi

这个脚本每10分钟检查一次/var/log分区的使用率,超过85%就发邮件告警,同时自动删除7天前的压缩日志作为应急措施。

审计策略的合规性验证

配置完成后需要验证审计覆盖度。用ausearch检查关键事件是否被记录:尝试修改/etc/passwd,然后用ausearch -f /etc/passwd -i查看是否有记录。用sudo执行几条命令,检查ausearch -k root-commands的输出。

对于合规要求严格的场景,建议定期运行审计规则完整性检查脚本,确保规则没有被意外修改或删除。把审计规则文件纳入版本控制,通过git diff就能快速发现变更。

日志审计和轮转不是一次性配置就完事的工作。业务量在增长,日志量也在变化,磁盘容量是有限的。每季度检查一次日志增长趋势,调整轮转参数,确保系统始终处于可控状态。这套机制运转起来后,服务器出问题时就不再是摸黑排查,而是有据可查。