半夜两点被值班电话叫醒,说线上服务全挂了。我揉着眼睛连上跳板机,第一件事不是去看监控大屏,而是先打开/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.log | Nginx 访问日志 | Web 访问审计、CC 攻击研判 |
/var/log/nginx/error.log | Nginx 错误日志 | 502、504、SSL 握手失败排查 |
/var/log/mysql/mysql.log | MySQL 通用日志 | 数据库请求审计 |
不同的发行版路径略有差异,比如 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 status
systemctl 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 _PID=12345很多人不知道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 来源 IP:
grep "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 _COMM=sshd --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.log;RedHat 系则在/var/log/secure里。关键字是COMMAND=:
Apr 11 03:20:11 web-01 sudo: admin : TTY=pts/0 ; PWD=/home/admin ; USER=root ; 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 _COMM=sudo也可以作为补充。有时候系统里装了 sudo 的审计补丁,日志会写到自定义文件里,这时再用journalctl _COMM=sudo去查,能拿到更全面的命令记录。
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 arch=b64 -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里这样调:
SystemMaxUse=500M SystemMaxFileSize=100M MaxRetentionSec=2weeksSystemMaxUse=500M是 journal 总容量上限,超过这个值后 journald 会按时间顺序清理旧日志;SystemMaxFileSize=100M是单个 journal 文件大小;MaxRetentionSec=2weeks保证日志最多保留两周,配合异地转发,本地存多久都问题不大。
改完配置文件记得重启服务:
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 的持久化和集中转发,绝大部分问题都能在半小时内定位到根因。我现在每接手一台新机器,都会先把日志落盘、转发、轮转这三件事检查一遍,别等真出事了才想起来,到时候哭都来不及。