服务器日志被删除怎么办?日志轮转、远程日志与审计方案

安全事件里有个很讽刺的规律:日志最需要的时候,往往正好不见了。攻击者拿到权限后的标准动作之一,就是清空 /var/log/ 下的记录——删掉 secure、auth.log、wtmp,甚至用 sed 批量注释掉日志里的可疑行。等你发现异常想追查时,发现"什么都没留下"。更常见的非恶意场景是:日志没做轮转,把磁盘写满,导致服务崩了;或者轮转策略写错,把重要日志覆盖了。

所以"日志被删怎么办"这个问题,答案分两半:一半是应急——如何判断日志是被删还是被覆盖、如何抢救残留证据;另一半是治理——如何通过轮转、远程集中、审计手段,让日志"删不掉、丢不了"。

这篇文章先讲日志被删后的应急判断与抢救,再讲三个层面的长效方案:logrotate 轮转防止磁盘爆和覆盖、rsyslog/journald 远程转发让本地删了远程还有、auditd 内核审计提供比普通日志更底层、更完整的记录。文中命令均以 root 或 sudo 执行。

一、先判断:日志到底是"被删"还是"正常滚动"

发现 /var/log/secure 或 auth.log 空的、少了一段,第一步不是急着重装,而是判断原因。有三种可能:日志正常轮转(老内容进了 .1、.gz)、文件被截断(内容清空但文件在)、文件被删除(inode 消失)。

# 1. 看目录里有没有轮转后的归档文件
ls -la /var/log/
ls -la /var/log/secure*          # RHEL 系:secure 及其 .1 .2 .gz
ls -la /var/log/auth.log*        # Debian 系

# 2. 看日志文件的时间戳(mtime/ctime),异常时间往往是关键线索
stat /var/log/secure
ls -la --time-style=full-iso /var/log/

# 3. 判断文件是否被"截断"——大小为0或突然变小
#    正常滚动会产生 .1 归档,截断则只有本体且变小
du -sh /var/log/

被删和被截断的区别很重要。如果文件还在但内容没了(大小归零),常见于 > /var/log/secure 或 truncate -s 0 这种"清空"操作。如果文件本身消失,那是 rm。两者在取证上意义不同:被截断的文件 inode 未变,有时还能通过文件系统恢复或页缓存拿到部分内容;被 rm 的文件 inode 已释放,恢复难度更大。

还有个容易被忽略的点:日志服务往往持有被删文件的文件句柄。如果你 rm 了 /var/log/secure 而 rsyslog 还在跑,那么文件虽然从目录里消失,但空间没释放,rsyslog 仍在往那个已删除的 inode 里写。这给了你一个抢救窗口:

# 找到仍然持有已删除日志文件句柄的进程
lsof +L1 | grep /var/log
ls -l /proc/*/fd/ 2>/dev/null | grep -i "deleted"

# 如果 rsyslogd 的 fd 指向 deleted 的日志,可以从 /proc/PID/fd/N 里把内容拷出来
# 假设 rsyslogd 的 PID 是 1234,fd 号是 7
cat /proc/1234/fd/7 > /root/recovered_secure.log

# 让服务重新打开一个新文件(触发生成)
systemctl restart rsyslog

这个技巧在应急响应里非常实用——攻击者以为删干净了,实际上内容还挂在进程的 fd 上,你直接就能捞出来。

二、抢救:从残留位置找证据

即使 /var/log/ 被清空,很多痕迹还散落在别处。应急时要系统地扫一遍这些"备份证据点":

# 1. 命令历史(攻击者常忘删,且被清时也可能有残留)
cat /root/.bash_history
history
# 检查 history 是否被清空或指向 /dev/null
echo $HISTFILE; ls -la ~/.bash_history

# 2. 内核环形缓冲区——比磁盘日志更底层,攻击者很难删
dmesg | tail -100
journalctl -k

# 3. systemd journal(如果没被清)
journalctl --since "2026-10-01" --until "2026-10-12"

# 4. 登录记录二进制文件(wtmp/btmp/lastlog)
last -F            # 成功登录
lastb              # 失败登录
lastlog            # 各账号最近登录时间

# 5. 进程、网络、计划任务的"当下快照"
ps auxf
ss -tunap
crontab -l; ls -la /etc/cron.d/

还有一个高级技巧:Linux 内核的审计子系统(auditd)如果开启了,它的日志可能在 /var/log/audit/audit.log,而且可以配置为不响应普通用户的删除。如果 auditd 也开了远程转发,那么即便本地全清,远端仍有完整记录。

# 查看 auditd 是否在运行、规则是否存在
systemctl status auditd
auditctl -l
ausearch -ts today | tail

下面这张表汇总了"本地日志被删"后,各证据点的可靠性,便于判断先后:

证据位置 是否易被删 留存时间 备注
/var/log/secure 等 极易 取决于轮转 首要目标
已删文件的进程 fd 中 至进程重启 抢救窗口有限
内核 dmesg 难 至重启 环形缓冲区
systemd journal 中 取决于配置 可持久化到磁盘
wtmp/btmp/lastlog 中 长期 二进制格式
auditd 日志 较难 取决于配置 可远程转发
远程日志服务器 极难 长期 最可靠

三、轮转:让日志不会"爆盘"也不会"被覆盖"

logrotate 是 Linux 日志管理的标配,它按大小/时间切割、压缩、保留、删除旧日志。用好它,能避免两种灾难:日志写满磁盘(服务全崩)、以及轮转配置错误导致重要日志被过早删除。

主配置文件 /etc/logrotate.conf,各服务的策略放在 /etc/logrotate.d/ 下。看一个典型配置:

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily                    # 每天轮转
    rotate 14                # 保留14份(约两周)
    missingok                # 文件不存在不报错
    notifempty               # 空文件不轮转
    compress                 # 压缩旧日志
    delaycompress            # 延迟一次再压缩(避免正在写的文件被压)
    sharedscripts            # 多个文件只执行一次脚本
    postrotate
        # 轮转后通知 nginx 重新打开日志文件
        [ -f /run/nginx.pid ] && kill -USR1 $(cat /run/nginx.pid) 2>/dev/null || true
    endscript
}

几个关键参数要理解清楚:rotate N 是保留的归档份数,超过就删最老的——这就是"日志被覆盖"的常见原因,如果设得太小(比如 rotate 3),出事后可能只查到三天前的记录。postrotate 脚本负责通知服务重新打开新日志文件,如果服务没收到通知,它会继续写老文件(现在被重命名成 .1 的那个),导致新日志不增长、归档日志不断变大,这是个经典坑。

安全相关的日志建议保留更久,并单独配置:

# /etc/logrotate.d/secure-audit  (自定义,覆盖默认策略)
/var/log/secure /var/log/audit/audit.log {
    daily
    rotate 90                # 安全日志保留90天,符合多数合规要求
    compress
    delaycompress
    missingok
    notifempty
    create 0600 root root     # 新建文件权限收紧
    sharedscripts
    postrotate
        /usr/bin/systemctl kill -s HUP rsyslog.service >/dev/null 2>&1 || true
    endscript
}

注意 create 0600 root root:轮转后重建的日志文件属主和权限要收紧,防止普通用户读取敏感日志内容。

测试轮转配置是必须的,-d 做 dry-run 不会真改文件,-f 强制立即执行一次:

# 调试模式,只显示会做什么,不实际执行(安全)
logrotate -d /etc/logrotate.d/nginx

# 强制执行一次(会真的轮转,谨慎)
logrotate -f /etc/logrotate.d/nginx

# 查看 logrotate 的状态记录(记录了每个文件上次轮转时间)
cat /var/lib/logrotate/logrotate.status

RHEL 系和 Debian 系的 logrotate 都由系统的 cron 或 systemd timer 驱动,安装方式不同:

# RHEL/CentOS/Rocky 系
dnf install -y logrotate
# 由 /etc/cron.daily/logrotate 驱动

# Debian/Ubuntu 系
apt install -y logrotate
# 由 systemd timer(logrotate.timer)驱动
systemctl status logrotate.timer

四、远程日志:本地删了,远端还有

前面所有本地手段,都有一个共同弱点:攻击者如果有 root,本地什么都能删。真正能解决"日志被删"问题的,是把日志实时转发到另一台机器——攻击者控制了业务服务器,也控制不了日志服务器。这是日志安全方案里最关键的一环。

rsyslog 做远程转发,服务端(日志服务器)和客户端(业务服务器)都要配置。

客户端 /etc/rsyslog.d/50-remote.conf:

# 将所有日志通过 TCP 发送到日志服务器(TCP 比 UDP 可靠)
*.* @@10.0.0.100:514

# 也可以只转发关键类别,减少带宽
authpriv.*  @@10.0.0.100:514
*.info;authpriv.none  @@10.0.0.100:514

注意 @@ 表示 TCP,@ 表示 UDP(不可靠,可能丢日志),安全场景一律用 @@。

服务端 /etc/rsyslog.conf 要打开接收:

# 加载 TCP 接收模块
module(load="imtcp")
input(type="imtcp" port="514")

# 按来源 IP 或主机名分目录存储,便于区分
template(name="RemoteLogs" type="string" string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")
*.* ?RemoteLogs

改完重启 rsyslog,并在服务端确认收到了日志:

# 服务端放行 514 端口
firewall-cmd --permanent --add-port=514/tcp && firewall-cmd --reload   # RHEL 系
ufw allow 514/tcp                                                       # Debian 系

systemctl restart rsyslog

# 客户端与服务端都要重启
systemctl restart rsyslog

# 服务端检查是否收到
ls -la /var/log/remote/
tail -f /var/log/remote/*/authpriv.log

systemd journald 也可以转发到远程 syslog:

# /etc/systemd/journald.conf
ForwardToSyslog=yes
# 让 journal 也持久化保存到磁盘(默认可能存在内存里,重启就没了)
Storage=persistent
systemctl restart systemd-journald
# 持久化后日志存放在 /var/log/journal/ 下

Storage=persistent 这一条很重要:很多系统默认 Storage=auto,如果 /var/log/journal/ 目录不存在,journal 就只存在内存里,重启后历史日志全部丢失。显式设为 persistent 才能保证重启后还能查。

远程日志这条链路也不是万无一失。要防止日志服务器本身被攻击导致全线失守,建议:日志服务器不对外提供业务、只开必要端口、业务服务器与日志服务器之间做网络隔离;日志写入后可以进一步转发到对象存储或日志平台做冷备。主流云厂商的对象存储都可以设置写一次读多次(WORM)或合规保留策略,配合日志投递,能实现"删不掉"的效果。

五、审计:比日志更底层的记录

普通日志是"应用层"记录,攻击者用 root 权限就能改。auditd 是内核审计子系统,它在系统调用层面记录事件,配置得当的话更难被绕过,是"日志被删"之后的第二道保险。

auditd 的规则文件在 /etc/audit/rules.d/ 和 /etc/audit/audit.rules。几条最实用的规则:

# /etc/audit/rules.d/audit.rules

# 监控敏感文件(如 passwd、shadow、sudoers)的写入和属性变更
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers

# 监控日志文件本身,谁动了日志就记录下来
-w /var/log/ -p wa -k log_tamper

# 记录所有执行 execve 的命令(量大,按需开启)
# -a always,exit -F arch=b64 -S execve -k exec

# 记录删除文件/重命名等操作
-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -k delete

加载规则并验证:

# 加载规则(会清空现有规则再加载)
augenrules --load

# 确认规则生效
auditctl -l

# 查询:谁改动了 passwd 文件
ausearch -k identity -ts today

# 生成报告
aureport --summary

注意 -w /var/log/ -p wa 这条规则会让每次日志写入都产生审计记录,在高流量服务器上日志量会非常大(经验上每天可能几个 GB),要配合轮转和磁盘规划,别因此把磁盘写满。生产环境里更务实的做法是只监控"删除"和"属性变更"(-p wa 里的 a 是属性、w 是写),或者只监控关键日志文件。

auditd 的日志同样要远程转发,这个在 /etc/audit/auditd.conf 和 audisp-remote 插件里配置,把审计事件直接送到远端,这样本地 audit.log 被删也不影响取证。

最后,防"日志被删"还可以加一层不可变属性。对不常写的日志文件,使用 chattr +a(只追加)或 chattr +i(完全不可改,连 root 都改不了,除非去掉属性):

# 让日志只能追加,不能删改(对正在写的日志文件可行)
chattr +a /var/log/secure

# 查看属性
lsattr /var/log/secure

# 取消属性(需要时)
chattr -a /var/log/secure

chattr +i 很猛——连 root 都不能直接删,必须先 chattr -i。但它也可能挡住正常轮转(logrotate 删除旧文件时失败),所以对日志文件一般用 +a(追加)而不是 +i,并且要理解清楚对轮转的影响再启用。

常见问题

Q:日志被清空了,还能恢复吗?
分情况。如果 rsyslog/journald 还持有已删除文件的 fd,可以从 /proc/PID/fd/N 抢救;如果只是被 truncate,有时能从文件系统层面恢复部分内容。但最可靠的恢复来源是远程日志服务器。所以别指望事后恢复,要在事前就把远程转发配好。

Q:怎么知道日志是被攻击者删的,而不是 logrotate 正常滚动?
看两点:一是目录里有没有对应的 .1、.gz 归档文件(正常滚动会有),二是文件时间戳和系统登录/操作记录是否异常。如果 /var/log/secure 大小为 0、也没有归档、且当时有异常登录,基本可判定是人为清空。

Q:wtmp、btmp、lastlog 也被删了怎么办?
这些是二进制记录,普通删除后同样可尝试从进程 fd 抢救,但更常见的是被清空。它们的价值在于记录登录历史,如果和远程日志里的 SSH 登录记录交叉比对,能还原攻击者的登录时间线。日常应该把 wtmp 也纳入监控和备份。

Q:远程日志会不会占用太多带宽?
取决于日志量。一台普通 Web 服务器每天日志量通常在几百 MB 量级,压缩后转发占用带宽很小(经验值,具体看业务)。高流量服务器可以只转发 authpriv、kern、cron 等关键类别,把 access log 这类大流量的留在本地用专门的日志采集器处理。

Q:开了 auditd 之后磁盘很快满了,怎么办?
审计规则越宽,日志越大。先把 -w /var/log/ 这类高写入量的规则去掉或收窄,只保留关键文件;配置 /etc/audit/auditd.conf 里的 max_log_file 和 num_logs 控制单文件和总大小;同时确保审计日志也走 logrotate 和远程转发,别让本地无限增长。

总结

  • 日志出问题先判断三种情况:正常滚动(有归档)、被截断(归零)、被删除(消失),三者处置方式不同。
  • 应急抢救优先找"进程持有的已删文件 fd"(lsof +L1、/proc/PID/fd)、内核 dmesg、journalctl、wtmp/btmp,这些是攻击者不易清理的残迹。
  • logrotate 要理解 rotate(保留份数,太小会覆盖历史)、postrotate(通知服务重开文件,漏了日志不增长)和 create(重建文件权限);安全日志至少保留 90 天。
  • 解决"日志被删"的根本手段是远程集中:rsyslog @@ TCP 转发到独立日志服务器,journald 设 Storage=persistent 并开启 ForwardToSyslog,远端再做备份。
  • auditd 在内核层面审计,规则更难绕过,适合监控 /etc/passwd、sudoers、日志目录等敏感目标;但要注意日志量,别把磁盘写满。
  • 对关键日志可用 chattr +a 限制为只能追加;完整方案是"本地轮转 + 远程转发 + 内核审计 + 不可变/冷备"四层叠加,才能做到删不掉、丢不了。