在很多 Linux 运维和应急响应场景里管理员遇到的第一类问题往往不是服务为什么挂了而是这台机器最近有没有人登录过、谁在什么时间从哪个 IP 登进来的、失败登录是不是突然变多了。这类问题的答案绝大多数就藏在三个不起眼的文件里/var/log/wtmp、/var/log/btmp和/var/log/lastlog。而能把这些二进制日志翻译成人能读的内容的命令正是last、lastb和lastlog。很多人对这三个命令的印象停留在面试会考的层面觉得last就是看一眼谁登录过lastlog就是查一下某个人上次登录时间。实际在排查入侵痕迹、做合规审计、定位异常账号、写自动化巡检脚本时这三个命令的用法要复杂得多也更容易踩坑。比如为什么last的输出里会出现reboot这样一条假用户记录为什么lastb明明有日志却提示文件不存在为什么lastlog在用户量大时执行得特别慢甚至直接报错这篇文章会从登录审计到底要解决什么问题出发把last和lastlog的数据来源、字段含义、参数组合、脚本化用法、日志轮转配置以及常见故障一次性讲清楚。读完之后你应该能独立完成一次基本的登录行为排查并且能把这几条命令固化进日常巡检脚本里而不是每次出事才临时翻手册。1. 这篇文章真正要解决的问题先说清楚一件事last和lastlog不是用来看日志的它们是用来回答特定审计问题的查询工具。区分这一点很重要因为如果你把它当成cat日志的替代品就会在参数上反复试错最后还是没得到想要的结论。在实际工作中这几个命令主要覆盖四类需求。第一类是日常运维确认比如刚给某个同事开了账号想确认他到底有没有成功登录过密码是不是已经改掉了第二类是异常排查比如怀疑有人用测试账号跑了不该跑的操作需要还原他的登录时间线和来源地址第三类是安全审计包括等保、内部合规检查里经常要求的账号登录记录留存与可追溯第四类是自动化巡检把登录信息采集进监控系统对异常时段登录、来源 IP 突变、短时间内大量失败登录做告警。这四类需求对命令的要求完全不同。日常确认只需要一条lastlog -u zhangsan异常排查需要last配合时间范围筛选和格式化输出安全审计要求你先搞清楚日志保留多久、轮转策略是什么自动化巡检则要求输出稳定、可解析能塞进脚本里被awk、grep二次处理。所以这篇文章的组织逻辑是先讲清楚数据从哪来不理解数据来源后面所有查不到的问题都无从下手再讲命令怎么用最后讲怎么把它变成一个可复用的排查流程。如果你只是想快速上手可以直接跳到第 4、5 节如果你曾经遇到过明明登录过但查不到记录的情况建议先读第 2 节。这里给一个明确判断last和lastlog的价值不在于命令本身有多复杂而在于它们背后那套 utmp/wtmp/lastlog 的记录机制。掌握机制你才知道记录什么时候会丢、什么时候会不准以及为什么有时候必须换用journalctl或审计日志来补充。2. last 与 lastlog 的底层数据来源与核心概念2.1 三个日志文件分别记录什么要理解这三个命令先要把 Linux 登录记录的文件体系理清楚。Linux 并没有把所有登录信息塞进一个大日志里而是按用途拆成了几个二进制文件各自负责不同的记录类型。文件路径记录内容由谁读取典型权限/var/run/utmp当前正在登录的用户会话who、w、users通常 664属组 utmp/var/log/wtmp历史登录、登出、系统启动与关机记录last通常 664属组 utmp/var/log/btmp失败的登录尝试lastb通常 600仅 root 可读/var/log/lastlog每个用户最后一次登录的信息lastlog通常 664属组 utmp这几个文件有一个共同特点它们都是二进制格式不能直接用cat、tail、vim正常查看。你用cat /var/log/wtmp看到的是一堆乱码这不是文件损坏而是设计如此。这种设计的初衷是写入效率高、定长记录便于快速索引代价就是必须依赖专门的工具来解析。这里有一个非常容易被误解的点utmp和wtmp名字很像但作用完全不同。utmp是当前状态谁现在在线wtmp是历史流水谁什么时候登录过、什么时候退出。很多新手会用who去查历史登录结果只看到当前会话然后怀疑日志丢了。记住这个区分查当前用who/w查历史用last。2.2 last 与 lastlog 的定位差异虽然都能查登录信息但last和lastlog的查询模型完全不一样这也是选择它们的关键依据。last是按时间倒序的事件流。它把wtmp里的记录从新到旧一条条列出来每一条是一个事件——一次登录、一次登出、一次重启。你可以按条数限制-n、按时间区间过滤-s/-t但它天然不适合回答某个用户最后一次登录是什么时候因为你需要自己去翻。lastlog是按用户聚合的快照。它是为每个 UID 维护一条记录直接告诉你这个用户最后一次登录的时间、终端和来源。它天然适合回答这个账号还活着吗这个账号多久没用了但不适合还原时间线因为它每个用户只保留最后一条。用一个类比last像是监控录像的回放lastlog像是每个员工工位上的最后打卡记录。要查某天发生了什么看录像要查某个人最近有没有来过看打卡记录。理解了这层差异很多选择困难就消失了。比如做账号清理时正确做法是lastlog一把梭找出所有Never logged in或者长期未登录的账号而做入侵溯源时必须用last拉时间线配合lastb看爆破痕迹。2.3 记录是怎么写进去的登录记录不是内核自动写的而是由登录程序主动写入。这个过程大致是用户通过 SSH、本地终端或图形界面登录时login、sshd、gdm等程序在认证成功后会向utmp和wtmp写入一条登录记录退出时再写入一条登出记录系统启动和关机时由init/systemd写入reboot和shutdown记录。这个机制带来两个重要推论。第一如果登录程序本身被绕过或者被替换记录可能缺失。比如某些非标准方式启动的会话、容器内的进程、直接调用su/sudo的切换未必都会在wtmp里留下标准记录。第二记录的可信度依赖于系统完整性。如果攻击者拿到了 root 并且清空了wtmplast自然什么都查不到这时候就需要靠远程日志服务器或者auditd来兜底。所以正确的认知是last/lastlog是第一现场方便快捷但不能作为唯一证据来源。生产环境里重要的登录审计一定要做日志外发。3. 环境准备与前置条件last、lastb、lastlog这三个命令在绝大多数 Linux 发行版上是预装的它们属于util-linux软件包。你可以先用下面的命令确认环境。# 确认命令是否存在及所属软件包 which last lastb lastlog # Debian/Ubuntu 系查询所属包 dpkg -S $(which last) # RHEL/CentOS/Rocky 系查询所属包 rpm -qf $(which last) # 查看 util-linux 版本 last --version如果命令不存在安装方式也很直接# Debian/Ubuntu sudo apt-get update sudo apt-get install -y util-linux # RHEL/CentOS/Rocky/AlmaLinux sudo dnf install -y util-linux需要提前说明的是util-linux的具体版本会随发行版而不同本文不绑定某个具体版本号所有示例都以通用行为为准。如果你在较新的发行版上发现lastlog的行为和本文描述有出入很可能是因为部分新版本已经开始用lastlog2逐步替代传统的lastlog实现遇到这种情况请以man lastlog的实际说明为准。权限方面需要注意两点。第一last读取的wtmp通常是 664 且属组为utmp普通用户一般也能读所以last通常不需要 root。第二lastb读取的btmp通常是 600 仅 root 可读因为失败登录记录里包含大量来源 IP 和尝试的用户名属于敏感信息普通用户执行会提示权限不足或者文件为空。# 确认日志文件是否存在及权限 ls -l /var/log/wtmp /var/log/btmp /var/log/lastlog # 典型输出类似 # -rw-rw-r-- 1 root utmp 70656 Jun 10 09:12 /var/log/wtmp # -rw------- 1 root utmp 12288 Jun 10 09:15 /var/log/btmp # -rw-rw-r-- 1 root utmp 292292 Jun 10 09:12 /var/log/lastlog如果你的环境里lastb提示btmp: No such file or directory这不是命令坏了而是这个文件从来没被创建过——只有发生过失败登录系统才会写入并可能创建这个文件。这一点在第 8 节的排查表里还会再提。4. last 命令核心用法拆解4.1 基础输出字段怎么读先跑一次最朴素的last把输出结构看明白。last -n 15典型输出大致是这样root pts/0 192.168.10.21 Mon Jun 10 09:12 still logged in ops pts/1 10.20.3.55 Mon Jun 10 08:47 - 09:05 (00:18) root pts/0 192.168.10.21 Mon Jun 10 08:40 - 08:58 (00:18) reboot system boot 5.10.0-28-amd64 Mon Jun 10 08:35 still running root tty1 Sun Jun 9 21:03 - 21:40 (00:37) wtmp begins Sun Jun 2 03:17:44 2024逐列拆开看第一列是用户名。这里出现的reboot并不是一个真实账号而是shutdown/reboot写入的特殊记录表示一次系统启动。同理你还会看到shutdown、runlevel这类伪用户记录。第二列是终端。pts/0、pts/1是伪终端通常对应 SSH 或图形终端里的会话tty1是本地控制台:0这类通常对应图形界面登录。第三列是来源。远程登录显示主机名或 IP本地登录这一列通常为空。第四列是登录时间。第五列开始是登出时间与持续时长。如果会话还在会显示still logged in如果是系统启动记录会显示still running。最后一行wtmp begins ...非常关键它告诉你日志的起点。如果这个时间比你预期的新很多说明日志被轮转过或者被清理过历史记录已经不在这个文件里了。这一行wtmp begins经常被忽略但它在排查时是判断查不到到底是没登录还是日志没了的第一手依据。4.2 常用参数组合last的参数不多但组合起来很实用。下面这几个是日常最常用的建议直接记住。# 只看最近 20 条记录 last -n 20 # 显示完整时间含年份、秒默认格式会省略年份 last -F -n 20 # 不做主机名反解直接显示 IP速度更快也更准确 last -i -n 20 # 不显示来源主机列输出更紧凑 last -R -n 20 # 宽格式输出避免长主机名或长用户名被截断 last -w -n 20 # 显示重启、运行级别变更、关机等系统事件 last -x # 只看指定时间之后的记录 last -s 2024-06-09 00:00:00 # 只看指定时间之前的记录 last -t 2024-06-10 00:00:00 # 指定时间区间 last -s 2024-06-09 00:00:00 -t 2024-06-10 00:00:00这里要重点说一下-i和-F。-i的作用是禁止把 IP 反解成主机名很多人在生产环境发现last执行慢得离谱原因就是 DNS 反解超时。加上-i之后输出的是纯 IP既快又避免了主机名带来的歧义。-F则是把默认省略的年份补全跨年排查时必须加否则你分不清是今年还是去年的记录。-x这个参数容易被低估。它会显示reboot、shutdown、runlevel记录在排查服务器是不是被人重启过时非常有用。正常执行last也会显示reboot但-x会展示得更完整。还有一个实用技巧last默认读/var/log/wtmp但如果系统做过轮转历史文件通常叫wtmp.1、wtmp.2.gz之类。轮转后的文件如果是明文可以直接用-f指定读取# 读取轮转后的历史文件 last -f /var/log/wtmp.1 -n 30如果文件被gzip压缩了last -f是读不了的需要先解压到临时位置再读。这一点在应急排查中很常见别指望last能直接读.gz。4.3 用 awk 做二次加工last的原始输出是给人看的列宽对齐但不适合直接做统计。实际巡检中更常见的做法是用awk把关键字段抽出来。#!/bin/bash # 文件路径/usr/local/bin/login-report.sh # 用途输出最近登录用户统计与来源 IP 分布 set -euo pipefail DAYS${1:-7} echo 最近 ${DAYS} 天登录用户 Top 10 last -i -s -${DAYS}days 2/dev/null \ | grep -vE ^(reboot|shutdown|wtmp|btmp|$) \ | awk {print $1} \ | sort \ | uniq -c \ | sort -rn \ | head -n 10 echo echo 最近 ${DAYS} 天登录来源 IP Top 10 last -i -s -${DAYS}days 2/dev/null \ | grep -vE ^(reboot|shutdown|wtmp|btmp|$) \ | awk $3 ~ /^[0-9]\.[0-9]\.[0-9]\.[0-9]$/ {print $3} \ | sort \ | uniq -c \ | sort -rn \ | head -n 10这个脚本有几个细节值得说明。第一grep -vE过滤掉了reboot、shutdown、wtmp begins这些非用户记录否则统计结果里会混进一堆伪用户。第二awk $3 ~ /^[0-9]\./用正则判断第三列是不是 IPv4 地址这样本地登录第三列为空的记录会被自然排除。第三-s -${DAYS}days这种相对时间写法比手写绝对时间方便last支持这种语法。运行方式和预期输出chmod x /usr/local/bin/login-report.sh /usr/local/bin/login-report.sh 7预期能看到类似这样的结果 最近 7 天登录用户 Top 10 12 root 5 ops 2 deploy 最近 7 天登录来源 IP Top 10 12 192.168.10.21 5 10.20.3.55 2 10.20.3.60如果输出为空先确认last本身能不能出结果再看时间范围是不是设得太窄最后确认wtmp是否被轮转或清理过。5. lastlog 命令核心用法与完整示例5.1 基础输出与字段含义lastlog的输出结构和last完全不同它是每个用户一行。lastlog典型输出Username Port From Latest root pts/0 192.168.10.21 Mon Jun 10 09:12:03 0800 2024 ops pts/1 10.20.3.55 Mon Jun 10 08:47:11 0800 2024 deploy **Never logged in** nginx **Never logged in**四个字段分别是用户名、终端、来源、最后登录时间。需要注意两个细节Never logged in表示这个账号自创建以来从未登录过而某些系统账号比如bin、daemon、sync通常也显示为从未登录这是正常现象不代表异常。lastlog最实用的价值在于账号清理。系统运行几年之后往往会残留大量已离职人员的账号、测试账号、临时开通的账号。用lastlog -t可以快速筛出长期未登录的账号# 只显示最近 30 天内登录过的用户 lastlog -t 30 # 显示 90 天之前登录过的用户即长期不活跃 lastlog -b 90 # 查看指定用户 lastlog -u deploy-t和-b是一对-t N是最近 N 天内-b N是N 天之前。做账号审计时lastlog -b 90的输出就是一份很直接的候选清理清单。5.2 批量导出未登录账号下面这个脚本把从未登录和长期未登录的账号整理成表格便于交给负责人确认。#!/bin/bash # 文件路径/usr/local/bin/audit-lastlog.sh # 用途导出从未登录及长期未登录的普通用户清单 set -euo pipefail IDLE_DAYS${1:-90} OUT_FILE/tmp/lastlog-audit-$(date %Y%m%d).txt # 取出 UID 1000 的普通用户不同发行版阈值可能不同请按实际调整 mapfile -t USERS (awk -F: $3 1000 $3 65534 {print $1} /etc/passwd) { echo 审计时间: $(date %F %T) echo 闲置判定阈值: ${IDLE_DAYS} 天 echo printf %-20s %-10s %-20s %s\n USERNAME PORT FROM LATEST echo ------------------------------------------------------------ for u in ${USERS[]}; do lastlog -u $u | tail -n 1 done } | tee $OUT_FILE echo echo 结果已保存到: $OUT_FILE运行方式chmod x /usr/local/bin/audit-lastlog.sh sudo /usr/local/bin/audit-lastlog.sh 90输出示例审计时间: 2024-06-10 10:22:31 闲置判定阈值: 90 天 USERNAME PORT FROM LATEST ------------------------------------------------------------ ops pts/1 10.20.3.55 Mon Jun 10 08:47:11 0800 2024 deploy **Never logged in** test01 **Never logged in**这里用lastlog -u逐个查询虽然比一次lastlog慢但好处是只输出普通用户不会把大量系统账号混进来。awk里的 UID 阈值 1000 是常见约定但有些发行版把普通用户起始 UID 设为 500脚本里已经注释说明落地前请按实际环境调整。5.3 关于 lastlog 的性能问题当系统用户数量很多比如几百上千个账号时直接执行lastlog可能明显变慢甚至报错。原因在于lastlog文件是按 UID 索引的定长记录文件程序会按 UID 顺序扫描遇到 UID 很大或者存在空洞时就会读很多无效区域。如果你遇到lastlog卡顿或者报类似 Couldnt find ... 的错误可以按下面的思路处理# 查看 lastlog 文件大小判断是否异常膨胀 ls -lh /var/log/lastlog du -h --apparent-size /var/log/lastlog # 确认系统内最大 UID帮助判断索引范围 awk -F: {print $3} /etc/passwd | sort -n | tail -n 1如果发现lastlog文件大小远超账号数量应有的规模通常是因为历史上有过超大 UID 的账号被删除后留下的空洞。这种情况下不要随意删除/var/log/lastlog删除等同于清空所有用户的最后登录记录会破坏审计链条。更稳妥的处理是确认业务允许后用truncate或者让系统在可控窗口内重建并且操作前做好备份。6. lastb 与失败登录排查lastb是last针对/var/log/btmp的专用版本输出格式和last基本一致但记录的是失败登录。# 需要 root 权限 sudo lastb -n 20 # 显示完整时间便于和时间线对齐 sudo lastb -F -n 20 # 统计失败登录来源 IP Top 10 sudo lastb -i 2/dev/null \ | grep -vE ^(btmp|$) \ | awk {print $3} \ | sort \ | uniq -c \ | sort -rn \ | head -n 10典型输出root ssh:notty 203.0.113.45 Mon Jun 10 03:22 - 03:22 (00:00) admin ssh:notty 203.0.113.45 Mon Jun 10 03:22 - 03:22 (00:00) root ssh:notty 198.51.100.7 Sun Jun 9 23:10 - 23:10 (00:00) btmp begins Sun Jun 2 03:17:44 2024这里的ssh:notty表示通过 SSH 发起但没有分配终端是典型的密码爆破特征。如果你的lastb输出里出现同一个 IP 在很短时间内对多个用户名连续失败基本可以判定为自动化爆破尝试。需要提醒的是lastb能看到的内容取决于 SSH 的配置。如果sshd_config里关闭了UsePAM或者调整了日志行为部分失败记录可能不会写入btmp。所以在做安全审计时不能只依赖lastb还要结合/var/log/secureRHEL 系或/var/log/auth.logDebian 系以及journalctl -u sshd来交叉验证。一个实用的组合排查命令# 对比失败登录与成功登录快速定位疑似被攻破的账号 echo 失败登录 Top 用户名 sudo lastb -i 2/dev/null | awk $1 ! btmp {print $1} | sort | uniq -c | sort -rn | head echo echo 成功登录 Top 用户名 last -i 2/dev/null | grep -vE ^(reboot|shutdown|wtmp|$) | awk {print $1} | sort | uniq -c | sort -rn | head如果某个用户名在失败榜上排第一同时在成功榜上也出现这就值得进一步核查要么是本人多次输错密码后成功要么是爆破成功。这时候要拉出这个账号的完整时间线确认登录来源 IP 是否和日常一致。7. 日志轮转与数据保留策略这是最容易被忽视、但出问题时最致命的一环。很多人查不到历史记录不是命令用错了而是日志早就被轮转覆盖了。wtmp和btmp都是定长记录文件会随着时间不断增长。大多数发行版通过logrotate对它们做定期轮转。典型的配置写在/etc/logrotate.conf里内容大致类似# 文件路径/etc/logrotate.conf片段 /var/log/wtmp { missingok monthly create 0664 root utmp minsize 1M rotate 1 } /var/log/btmp { missingok monthly create 0600 root utmp minsize 1M rotate 1 }这段配置的关键信息是rotate 1意思是只保留一份历史文件。也就是说wtmp每个月轮转一次旧文件变成wtmp.1再下个月轮转时wtmp.1被覆盖。这意味着默认情况下你可查的登录历史可能只有一两个月。对于需要满足合规留存要求的环境这个保留周期通常是不够的。调整策略时要注意权衡。把rotate调大能保留更久但wtmp增长很快在登录频繁的跳板机或 CI 构建机上一个月的记录可能就有几十兆甚至更大。更合理的做法是本地保留适度周期 定期外发归档#!/bin/bash # 文件路径/usr/local/bin/archive-login-logs.sh # 用途每周归档登录日志到指定目录保留 180 天 set -euo pipefail ARCHIVE_DIR/data/audit/login-logs KEEP_DAYS180 STAMP$(date %Y%m%d) mkdir -p $ARCHIVE_DIR # 归档当前的 wtmp 与 btmp复制而非移动避免影响正在写入的文件 cp -a /var/log/wtmp ${ARCHIVE_DIR}/wtmp.${STAMP} 2/dev/null || true cp -a /var/log/btmp ${ARCHIVE_DIR}/btmp.${STAMP} 2/dev/null || true # 压缩旧归档 find $ARCHIVE_DIR -type f -name wtmp.* ! -name *.gz -mtime 1 -exec gzip -f {} \; find $ARCHIVE_DIR -type f -name btmp.* ! -name *.gz -mtime 1 -exec gzip -f {} \; # 清理超期归档 find $ARCHIVE_DIR -type f -name *.gz -mtime $KEEP_DAYS -delete echo [$(date %F %T)] archive done, keep ${KEEP_DAYS} days配合 crontab 使用# 文件路径/etc/cron.d/archive-login-logs # 每周一凌晨 3 点执行 0 3 * * 1 root /usr/local/bin/archive-login-logs.sh /var/log/archive-login-logs.log 21这里用cp -a而不是mv是因为wtmp正在被登录程序写入直接移动会导致写入句柄指向一个已经不存在的路径可能造成后续记录丢失。复制一份出来归档是更安全的做法。归档文件本身包含敏感信息用户名、来源 IP、时间存放目录务必收紧权限建议设置为700并限定属主。如果你需要更长期、更可靠的审计能力仅靠本地文件是不够的。考虑接入集中式日志系统把登录事件实时推送到独立的日志服务器这样即使本机日志被清理外部仍有留存。8. 常见问题与排查思路下面这张表覆盖了实际使用中最高频的几类问题。遇到查不到报错结果不对时建议按顺序对照排查。问题现象可能原因排查方式解决方案last输出只有当前会话没有历史wtmp被轮转或清理或刚重启过查看输出末尾wtmp begins的时间检查 logrotate 配置调整保留周期启用日志外发lastb提示btmp: No such file or directory系统从未发生过失败登录文件未创建ls -l /var/log/btmp确认文件是否存在属正常现象如需监控可先手动touch并设权限 600lastb输出为空但确实有失败登录当前用户无权限读取btmp用sudo lastb重试检查文件权限chmod 600 /var/log/btmp属主 root用 sudo 执行last执行非常慢来源 IP 被反解为主机名DNS 超时加-i参数对比执行速度日常脚本统一加-i避免反解输出中主机名被截断看不全默认列宽限制观察是否被...截断加-w宽格式输出lastlog输出大量Never logged in系统账号或长期未使用的普通账号结合/etc/passwd的 UID 范围过滤对普通账号做清理评估系统账号忽略lastlog执行卡顿或报错lastlog文件因超大 UID 空洞而膨胀ls -lh /var/log/lastlog看文件大小备份后评估重建切勿直接删除last -f读取历史文件失败文件被 gzip 压缩file /var/log/wtmp.1确认格式先解压到临时目录再用-f指定跨年记录时间分不清默认格式省略年份观察时间列是否只有月日加-F显示完整时间容器内执行last无任何记录容器有独立命名空间不共享宿主机wtmp对比宿主机与容器内输出审计应在宿主机执行或挂载宿主机的/var/log表格里最后一条值得单独说明。在容器化环境里/var/log/wtmp往往是容器镜像自带的空文件或者干脆不存在容器内的last自然查不到任何东西。这不是命令的问题而是隔离机制的正常表现。做登录审计时应该在宿主机层面执行或者在容器启动时把宿主机的日志目录以只读方式挂载进去。但要注意多容器共享同一个wtmp会带来写入竞争一般不推荐。另外如果排查过程中发现wtmp begins的时间点非常新鲜比如系统已经运行半年但wtmp只从三天前开始记录这时候第一反应应该是怀疑日志被清理过而不是这三天没人登录。这种情况下要立刻去核对logrotate的执行记录、/var/log/messages或journalctl里的相关操作痕迹。9. 最佳实践与工程建议把命令用对只是第一步真正让登录审计产生价值的是把它变成一套稳定的日常机制。下面这几条是实践中反复验证过的建议。第一区分日常查询和审计取证两套用法。日常查询追求快last -n 20 -i就够了审计取证追求完整和可追溯必须用-F显示完整时间必须记录wtmp begins这个边界必须把原始输出保存下来。两者不要混用否则要么太慢要么证据不完整。第二脚本里统一加-i和-F。自动化脚本最怕的就是不确定性。-i消除了 DNS 反解带来的耗时波动和结果歧义-F消除了跨年时的时间歧义。这两个参数加上去输出的稳定性和可解析性会明显提升。第三过滤伪用户记录要形成固定套路。reboot、shutdown、runlevel、wtmp begins、btmp begins这些都不是真实用户做统计时如果不过滤结果就不可信。建议在团队内统一一份过滤规则写进公共脚本函数里复用。第四日志保留周期要和合规要求对齐。默认的rotate 1只能撑一两个月很多合规要求是六个月甚至更久。上线前先确认要求再决定是调整 logrotate 还是做外部归档。调整时注意磁盘容量跳板机这类高频登录的机器要单独评估。第五敏感信息要控制可见范围。btmp和lastlog里包含用户名和来源 IP属于个人信息范畴。文件权限保持 600 或 664 且属组受控归档目录权限收紧脚本输出不要随手贴到公开渠道。做审计报告时来源 IP 可以考虑做脱敏。第六不要把last/lastlog当作唯一证据源。它们记录的是登录行为不记录登录后做了什么。真正的安全审计需要多层证据wtmp/btmp看登录auth.log/secure看认证细节auditd看关键文件和高权限操作history看命令历史但可被篡改。多层交叉才能形成可靠结论。第七变更前先备份。涉及/var/log/lastlog重建、logrotate 配置调整、日志清理这类操作一律先备份再动手。清理登录日志在很多合规体系里本身就是需要审批的操作别在生产环境上直接试。第八把巡检结果接入监控。光有脚本不够要有告警。可以对非工作时间登录新出现的来源 IP单 IP 短时间失败次数超阈值设置规则把last和lastb的输出定时采集后做比对。这比事后翻日志有意义得多。10. 小结与继续深入的方向回到最开始的问题last和lastlog到底解决了什么它们解决的是把二进制的登录流水翻译成可判断的信息这件事。last给你时间线lastlog给你账号活跃度快照lastb给你失败尝试的痕迹。三个命令加起来能覆盖大部分日常运维确认和初级安全排查场景。但也要清楚它们的边界。它们只记录登录这个动作不记录登录之后发生了什么它们依赖本机文件文件被清理就失去意义它们的精度受限于登录程序的写入行为某些非标准会话可能不留痕。理解这些边界比记住所有参数更重要。如果要继续深入有两个方向值得投入。一个方向是补充认证层日志的排查能力把wtmp/btmp和sshd日志、PAM 日志串起来看这样能还原出更完整的认证过程。另一个方向是把审计能力做成体系包括集中式日志采集、登录行为基线、异常检测规则让查日志从被动响应变成主动发现。最后留一个可以直接落地的建议花十分钟在你负责的机器上跑一遍last -F -n 20和sudo lastb -n 20看看wtmp begins是哪天看看失败登录里有没有陌生 IP。这个动作成本极低但往往能提前发现一些平时完全没注意到的情况。命令本身不复杂难的是养成定期看一眼的习惯。