半夜两点被值班电话叫醒说线上服务全挂了。我揉着眼睛连上跳板机第一件事不是去看监控大屏而是先打开/var/log/messages和journalctl -xe。为什么因为监控只告诉你“什么挂了”日志才能告诉你“为什么挂”。干运维这些年我越来越确信一件事——Linux 系统日志就是一台机器的病历本和黑匣子故障排查、安全审计、渗透复盘哪样都离不开它。这篇文章我不打算写成命令手册的堆砌而是想把自己实际排障、做安全加固、整理攻击链路的经验串起来把 Linux 日志体系的底层逻辑、高频排查路径、以及容易被忽略的坑一次性讲透。适合刚入门想要建立排查思路的新人也适合有一定经验但想把日志用得更系统的运维和安全工程师。1. 先盘清楚家底Linux 日志到底存在哪又是谁在写很多人一上来就背日志路径这个在案发时根本没用。你得先理解日志的产生源头和流转路径才能知道在什么场景去哪里找。1.1 日志的三大源头内核、系统服务、用户态应用Linux 下的日志从来源上分主要就三路内核日志由内核自己产生包括硬件驱动报错、文件系统错误、OOM 杀进程、网络栈异常等。最早统一由klogd接管现在基本走/dev/kmsg最后进入journald或者/var/log/dmesg。系统服务日志sshd、crond、rsyslog、NetworkManager 这些由 systemd 托管或传统 SysV 管理的服务是日常排障最常翻的东西。用户态应用日志Nginx、MySQL、Java 应用这些跑在用户态的进程自己写的日志多半不走系统日志通道而是写到各自的日志目录里。记住上面的分类你在排查时就会顺着问三个问题是内核有问题还是系统服务有问题还是应用自己有问题方向对了定位就快。1.2 常用日志位置速查表以下是我自己经常用的日志路径你可以直接收藏日志文件对应内容适用场景/var/log/messages系统级通用日志RedHat/CentOS 系系统异常、服务崩溃的兜底入口/var/log/syslog系统级通用日志Debian/Ubuntu 系同上各发行版命名不同/var/log/secure安全认证日志RedHat/CentOS 系登录成败、sudo 提权记录/var/log/auth.log安全认证日志Debian/Ubuntu 系同上/var/log/dmesg内核环形缓冲区日志硬件故障、驱动加载、OOM/var/log/boot.log系统启动日志开机过程卡住、服务启动失败/var/log/cron计划任务执行日志cron 任务不执行时排查/var/log/nginx/access.logNginx 访问日志Web 访问审计、CC 攻击研判/var/log/nginx/error.logNginx 错误日志502、504、SSL 握手失败排查/var/log/mysql/mysql.logMySQL 通用日志数据库请求审计不同的发行版路径略有差异比如 Ubuntu 上没有/var/log/secure而是/var/log/auth.log。这个看起来是小问题但很多新人换发行版后排障就卡在这一步。1.3 journald 和 rsyslog 是两套并行体系必须分清这是大多数人一开始最容易晕的地方。systemd-journald 负责采集rsyslog 负责采集加转发归档二者其实是并列的。现在的发行版里rsyslog 收集到的许多内容就是通过 socket 从 journald 里读过来的然后按配置写入/var/log/目录下的文件。所以你用journalctl查到的内容和tail /var/log/messages查到的内容大部分是重合的但journald 会更全因为它还收集了 stdout、stderr 以及内核对/dev/kmsg的写入。我建议你在排查时先journalctl因为它能看到服务进程直接打到标准输出和标准错误上的内容这个 rsyslog 默认是不会落盘的。等需要做长期归档、集中转发时再依赖 rsyslog 的规则链。2. 故障排查日志不是翻出来的是顺着线索找出来的很多朋友问我为什么我出了问题翻日志也找不到原因其实不是日志没记是你没按线索去翻。日志排查讲究一个顺序和链路。2.1 我排障时遵循的三条主线我把排障场景拆成三条主线每条主线对应不同的日志源和关键词内核与硬件层出现重启、死机、性能骤降、磁盘异常先看/var/log/dmesg和journalctl -k关键词重点留意error、fail、panic、oom、I/O error。系统服务层某个系统服务sshd、crond、NetworkManager状态异常直接用systemctl status 服务名加journalctl -u 服务名这比去/var/log/messages里瞎翻快得多。应用与业务层Nginx、MySQL、Redis 这类应用它们的日志格式五花八门去各自的日志目录找错误级别以上的输出通常能直接看到报错行。不分层排查你就容易陷入在 messages 里 grep 一晚上、最后发现是 Nginx 配置写错了的尴尬。2.2 实战案例一次半夜 OOM从 dmesg 里现出原形有一回客户反馈业务集群里某个节点突然无响应重启后短暂恢复又挂。监控面板显示 CPU、内存都被拉满但主业务进程的日志里没有任何异常输出。我登录机器后先跑了dmesg -T一眼看到这段[Thu Apr 11 03:12:47 2024] Out of memory: Killed process 2314 (java) total-vm:8596 564kB, anon-rss:2635124kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:4628k B oom_score_adj: 0 [Thu Apr 11 03:12:47 2024] oom_reaper: reaped process 2314 (java)OOM又是 OOM。Java 进程被内核 OOM Killer 直接杀了。这时再去看业务日志已经来不及了进程都死了还写什么日志。这就是我强调一定要先查内核日志的原因——进程被强制杀掉时的“遗言”只有dmesg里记录得最清楚。找到 OOM 之后我继续看是哪个进程吃掉了内存用top按内存排序同时配合journalctl --since 1 hour ago | grep -i memory追查是否有进程在持续膨胀。最后定位到一个连接数异常增长的 PHP-FPM 池把pm.max_children调小、加上进程重启策略问题才算彻底结束。2.3 服务起不来别只盯着 systemctl statussystemctl status输出的那几行信息确实直观但它通常只显示最近几条日志很容易误导人。比如 sshd 启动失败时status 显示failed但也可能只给出“Permission denied”这种模糊信息真正的根因还在更早的日志里。我的习惯是三步走systemctl status sshd journalctl -u sshd --since today journalctl -u sshd -p err第一个命令看状态第二个命令看完整日志流第三个命令只捞错误级别以上的记录。很多新人看完第一个就直接在网上搜“sshd failed”怎么搜也搜不到对应答案其实根因很可能只是/etc/ssh/sshd_config里一个非法配置项错误日志在第二、三步里写得清清楚楚。同理排 Nginx 的 502 时我从来不去 Nginx 的 access log 里找答案因为 502 不是 Nginx 自己产生的它只是给客户端返回了一个上游错误。这时候必须去看后端的应用日志、PHP-FPM 日志甚至是 MySQL 的慢查询日志才能真正定位。2.4 journalctl 的高频用法我都给你盘出来journalctl参数很多我这里只列我真正常用的组合# 查看系统启动后的全部日志相当于把 messages 从头看一遍 journalctl -b # 查看上一次启动的日志对比崩溃前后的状态 journalctl -b -1 # 查看最近 30 分钟的日志 journalctl --since 30 min ago # 按优先级过滤比较常用的是 err 和 warning journalctl -p err --since 1 hour ago journalctl -p warning -b # 跟踪某个 unit 的实时输出相当于 nginx error.log 的 tail -f journalctl -u nginx.service -f # 看内核日志等价于 dmesg 的更多格式版本 journalctl -k # 按进程过滤比如盯紧某个 PID 的所有输出 journalctl _PID12345很多人不知道journalctl -b后面可以跟负数这个在排查“重启前发生了什么”时非常有用。进程崩溃往往在重启前瞬间系统重启后你再journalctl -b看的是当前启动周期等于把案发现场丢了。2.5 日志时间线的坑时区不一致会误判这算是我踩得最深的一个坑。默认情况下journald 会用系统本地时区记录时间而 rsyslog 写文件时有一套自己的时间格式如果系统时区改过甚至/var/log/messages里的时间和dmesg -T显示的时间对不上。排查时如果把时间线拉错了很容易得出完全错误的结论。建议在排障开始前先确认三处时间是否一致date timedatectl dmidecode -s bios-release # 顺带看看硬件时间如果发现时间不同步先timedatectl set-local-rtc 0把硬件时钟改成 UTC再用chronyc makestep校准系统时钟不然所有日志时间线的关联都会失真。3. 安全审计从认证日志里读出攻击者的脚印安全审计和故障排查看日志的角度完全不一样。故障排查是“我的服务为什么挂了”安全审计是“系统里有没有不该发生的事”。Logrotate 里的轮转文件、auth 日志里的异常登录、sudo 记录里的越权行为都是重点盯防目标。3.1 认证日志里必须认识的几个关键字段以/var/log/secure为例一条典型的 SSH 登录日志长这样Apr 11 03:12:47 web-01 sshd[2314]: Failed password for root from 203.0.113.5 port 45678 ssh2 Apr 11 03:13:01 web-01 sshd[2314]: Accepted publickey for root from 203.0.113.5 port 45678 ssh2你不需要看懂全部只需要抓住三个要素时间、来源 IP、操作结果。Accepted代表认证成功Failed代表认证失败Invalid user代表尝试登录一个不存在的账户Connection closed by authenticating user往往代表登录成功了但在认证前被掐断也可能是试探行为。我在做安全基线检查时第一步一定是统计当天Failed password的 TOP 10 来源 IPgrep Failed password /var/log/secure | awk {print $(NF-3)} | sort | uniq -c | sort -nr | head -10如果同一 IP 来源的失败次数超过 50 次基本可以判定为暴力破解扫描接下来就是封 IP、检查有无同一来源的成功登录记录。这个检查动作我几乎每周都会做一次也是客户安全审计最基础的一项。3.2 暴力破解与撞库的特征识别仅统计失败次数还不够攻击者不会只用一台机器。我在实际审计中见过很多更隐蔽的尝试多 IP 轮换端口扫描式登录、每个 IP 只试两三次、用正常用户名配合大量密码词表。这时候如果还只看单一来源 IP就很容易漏掉。我的办法是把“谁在什么时间用了什么用户名从哪些 IP 登录”做成关联分析。有 SIEM 平台的直接做关联规则没有的话也可以用一条命令粗筛journalctl _COMMsshd --since 1 hour ago | grep Failed password | sed -E s/.*sshd\[([0-9])\]: (Failed password for) (invalid user )?([^ ]) from ([0-9.]).*/\4 \5/ | sort | uniq -c | sort -nr | head -20重点关注的模式有两个同一个用户短时间内从多个不相关 IP 出现Failed password这基本可以断定是横向移动后批量尝试。某个用户先是Failed password后来Accepted说明你可能已经被爆破成功必须立刻排查该用户的登录来源、命令历史、有无反弹 Shell、有无异常进程。尤其要注意Accepted publickey因为很多攻击者在拿到一台机器后会用ssh-keygen生成自己的密钥然后写入authorized_keys之后就可以绕过密码静默登录。我每次做安全审计第一件事就是检查所有用户的~/.ssh/authorized_keys文件有没有异常条目。3.3 sudo 记录与提权痕迹别只看登录日志登录日志只能看到“谁进来了”看不到“进来之后干了什么”。想做完整的权限审计sudo日志必须看。在 Debian/Ubuntu 系默认的认证日志里sudo 的执行记录也会写进/var/log/auth.logRedHat 系则在/var/log/secure里。关键字是COMMANDApr 11 03:20:11 web-01 sudo: admin : TTYpts/0 ; PWD/home/admin ; USERroot ; COMMAND/bin/vi /etc/passwd这条日志意味着 admin 用户通过 sudo 以 root 身份编辑了/etc/passwd。凡是出现/bin/su、/etc/sudoers、/etc/shadow、/etc/ssh/sshd_config、/usr/bin/passwd这类路径的 sudo 命令都要当成高危动作看待。除了 sudo 本身的记录journalctl _COMMsudo也可以作为补充。有时候系统里装了 sudo 的审计补丁日志会写到自定义文件里这时再用journalctl _COMMsudo去查能拿到更全面的命令记录。3.4 auditd系统自带的行为审计利器说实话我最早接触日志偏故障排查对安全审计没什么概念直到遇到一次“不知道谁把文件改了”的投诉。排查时发现 SSH 登录日志只有一条正常登录但/etc/passwd莫名多了一个 UID 0 的账户。后来我用auditd复查一查一个准。auditd是 Linux 内核层面的审计框架能记录到用户空间的行为对应到哪个进程、哪个用户、哪个时间。它和普通日志最大的区别是普通日志是应用程序自愿写的auditd 是内核强制记录的。以下是我实际项目里用过的一段最小规则auditctl -w /etc/passwd -p wa -k passwd-watch auditctl -w /etc/shadow -p wa -k shadow-watch auditctl -w /etc/sudoers -p wa -k sudoers-watch auditctl -a always,exit -F archb64 -S execve -k command-exec第一条规则监控/etc/passwd的写入和属性变化第二条监控 Shadow 密码文件第三条监控 sudo 配置第四条则记录所有命令执行-k command-exec是自定义的标记。审计事件会写到/var/log/audit/audit.log我需要回看某一次命令执行时直接按这个时间关键词过滤ausearch -k command-exec --start today设定-w时要评估性能开销尤其是-S execve这种全局命令审计在高频生产环境下会产生海量日志。我的经验是先在一台机器上试运行一个周期观察落盘速率再决定要不要推广。4. 渗透复盘视角攻击者与防守者眼中的同一份日志渗透测试复盘其实可以从两个完全不同的角度去看同一份日志攻击者看日志是为了“隐身”防守方看日志是为了“还原”。这两个视角一旦拉齐就能把防御策略做到位。说明以下内容仅为安全防护与授权范围内的渗透测试复盘思路所谓“攻击者视角”是为了理解对抗思路所有操作均应在合法授权和合规测试场景下进行请勿用于未经授权的系统。4.1 攻击者进入后为什么急着碰日志通常碰哪些很多攻击者在拿到一台机器权限之后第一件事不是翻业务数据而是清理现场。原因很简单——日志暴露了他进来的路径、用了什么账户、执行了什么命令、目标服务器是什么版本都写在日志里。攻击者最常碰的目标基本就是/var/log/auth.log或/var/log/secure因为他登录的路径在这里留了痕迹。/var/log/wtmp、/var/log/btmp、/var/log/utmp这里记录着登录和登出会话。当前 shell 的history文件~/.bash_history这里记录着他敲过的每一条命令。如果他有 root 权限甚至可能直接用shred -z覆写日志文件配合日志轮转把痕迹冲掉。更隐蔽的做法是只删除自己会话时间窗口内的日志行保留其它部分让日志看起来依然“正常”。这也是为什么防守方要做日志异地实时转发不能让日志只在被入侵机器本地留一份。4.2 防守方如何重建攻击链路时间线拉齐 跨源比对我在一次合法授权测试中的复盘是这样做的拿到一台被控机器后先把本地的auth.log、dmesg、journalctl全部按时间排序建立一条以秒为单位的攻击时间线。然后从/var/log/wtmp里拉出所有登录会话找出攻击者进入了哪些用户、在哪些时间段在线。接着做跨源比对Look 一下在那段时间里auth.log里的登录事件同时从~/.bash_history文件里还原出该用户在 shell 里执行的命令。再把该时间段内落地的文件比如/tmp下的脚本、/root/.ssh/authorized_keys的新增条目和 bash history 里的操作一一对应起来。一套完整的攻击链路基本就能还原个七八成。这里有个非常关键的点日志本身也可以被改。如果你在一台被控机器上做复盘它给出的线索只能作为参考不能当作绝对证据。真正能定论的是异地保存的日志所以在企业安全建设中把日志实时转发到独立的日志平台是底线要求而不是可选项。4.3 本地日志被清理后还能从哪里找线索如果攻击者清理得很彻底auth.log里几乎一片空白这时候别急着放弃系统里还有几个“不太好清干净”的地方内核日志dmesg、/var/log/kern.log攻击者加载 LKM Rootkit 时内核日志里可能出现异常模块加载记录。进程时间线/proc目录残留如果进程还在运行可以直接从/proc/pid/cmdline拿到完整启动命令。临时文件落盘shell reverse shell 通常会写入/tmp、/dev/shm这些目录下的小文件别看不上眼很多时候就是攻击工具的载体。系统时间线find / -newer /etc/hostname可以列出特定时间后被修改过的文件配合日志窗口能快速找到被修改的可疑文件。另外值得一提的还有PS1或者alias里被注入的恶意命令它们不会出现在 bash history 里但会躲在内核日志、进程列表和文件时间线中。这也是为什么复盘时不只看一种日志而是把所有线索交叉在一起判断。5. 日志持久化与实时集中收集好几个坑我已经替你踩过了最后这部分看着不像日志分析但恰恰是真正影响“关键时刻能不能查到日志”的幕后功臣。没有持久化和集中收集前面所有方法论都是空中楼阁。5.1 journald 默认不写磁盘这是一个大坑journald默认把日志写到内存临时文件/run/log/journal/机器一重启全丢。你没看错就是默认全丢除非你专门开启了持久化。这个坑我印象太深了——有次排查一个内存奔溃的问题好不容易拿到现场可一重启systemd 的所有日志都被清空等于白跑一趟。从那以后我每装一台 Linux 机器第一件事就是执行这几条命令mkdir -p /var/log/journal systemd-tmpfiles --create --prefix /var/log/journal systemctl restart systemd-journald开启持久化之后journald 会把日志写入/var/log/journal/就再也不会因为重启而丢了。5.2 journald 的存储上限配置持久化开启后还有一个隐患journald 是无冕之王它会把所有内容都攒下来不管空间够不够。如果不限制上限时间长了可能涨到大几十 GB反过来把系统盘给吃满那就得不偿失了。我一般会在/etc/systemd/journald.conf里这样调SystemMaxUse500M SystemMaxFileSize100M MaxRetentionSec2weeksSystemMaxUse500M是 journal 总容量上限超过这个值后 journald 会按时间顺序清理旧日志SystemMaxFileSize100M是单个 journal 文件大小MaxRetentionSec2weeks保证日志最多保留两周配合异地转发本地存多久都问题不大。改完配置文件记得重启服务systemctl restart systemd-journald5.3 rsyslog 的文件轮转与集中转发journald 解决的是采集和读取rsyslog 解决的是落盘与转发。两者经常要配合着用。对/var/log/下的文件rsyslog 默认已经配了 logrotate 的每日轮转但你最好先看一下/etc/logrotate.d/下的配置确认轮转周期是 day 还是 size比如 sshd 日志可以配成按大小轮转防止超大文件。保留周期是否满足审计要求比如安全审计通常要求保留 180 天以上。轮转后是否执行了reload rsyslog不然 rsyslog 继续往已轮转的文件里写可能报文件句柄错误。日志的集中转发最简单的方式是 rsyslog 的远程日志# 在 /etc/rsyslog.conf 或 /etc/rsyslog.d/remote.conf 中配置 *.* log-collector.example.com:514这条配置把本机所有 syslog 消息转发到log-collector的 UDP 514 端口。UDP 适合日志量不大、允许丢失的场景生产环境建议用 TCP加log-collector.example.com:514可靠性更高。现在企业里更常见的是用 Loki、ELK 一类的日志平台做集中存储与检索相关配置网上很多我这里只说一句——不管用什么平台一定要把转发出口做在系统层而不是只靠业务进程自己上报否则一旦应用进程被攻击者接管它可以选择不报或者报假数据。5.4 定时任务日志里的低频陷阱最后提一个 cron 日志的坑。很多人觉得 cron 日志没什么用但其实/var/log/cron里记录着所有计划任务的执行历史和结果。如果某个定时备份脚本凌晨每天执行某一天突然没有日志输出那通常意味着任务没被触发或者脚本第一行就报错了。journalctl -u crond --since today也能看到 crond 服务的运行状态我建议把备份类脚本的日志独立输出到自定义文件并在脚本里加上失败退出码判断这样排障时就不用大海捞针。我在实际项目中最常做的一件事就是把每分钟的 cron 执行记录和 auth 日志、应用日志做时间轴比对这种低频痕迹往往能帮你把“看似正常”的故障和真实的安全事件联系在一起。Linux 日志这东西说复杂可以很复杂说简单其实就一条逻辑先知道日志在哪再知道怎么看最后理解每个字段背后的含义。把故障排查的三条主线和安全审计的四个抓手记牢再配上 journald 的持久化和集中转发绝大部分问题都能在半小时内定位到根因。我现在每接手一台新机器都会先把日志落盘、转发、轮转这三件事检查一遍别等真出事了才想起来到时候哭都来不及。
