last 命令是 Linux 运维里查登录记录的经典工具,也是排查服务器异常登录、账号滥用、重启历史时绕不开的一把“考古铲”。很多人只会在终端敲一个last,看到一堆用户名、日期、IP 就完事,对这个命令背后的文件格式、日志轮转机制、字段含义却不太清楚。这篇博文我打算用大量实际可复现的示例,把 last command 的常见用法、输出解读、故障排查和自动化审计思路一次讲透,内容适合刚接触 Linux 的新手,也适合希望补全登录审计细节的运维和开发同学。
1. 项目概述:一次登录,一份日志,一条命令
1.1 last 命令是什么,它到底在查什么
last是 Linux 系统里用来查看用户登录历史的命令,它本身不做记录,而是读取系统维护的 wtmp 日志文件。大多数发行版把这个文件放在/var/log/wtmp,文件里保存的是历史登录、退出、系统重启、时间变更等事件记录。可以说,wtmp 是系统登录行为的“账本”,而last是把这个二进制账本翻译成人话的查看器。
我第一次接触 last 是在排查一台测试服务器的不明登录行为时,当时/var/log/secure里没有异常,但last一敲出来就发现有几条来自陌生 IP 的 root 登录记录。从那之后再接手新服务器,我开完机第一件事就是跑一遍last -n 20,看看这台机器此前被谁登录过、从哪登录的、有没有正常的重启周期。这个习惯一直保留到今天,last 虽然简单,但在系统安全巡检里确实是出镜率最高的命令之一。
需要提醒的是,last 和who、w不是同一类工具。who看的是当前谁在线,w还能看出每个在线用户正在执行的命令,而 last 看的是历史记录。三个命令各管一段,配合起来才能还原一台机器的完整登录图景。
1.2 数据来源与文件格式:别把 wtmp 当文本文件读
wtmp 是二进制文件,不能用cat或vi直接查看。它的底层结构是一系列固定长度的记录,每条记录对应一次登录会话的开始或结束,字段包括用户名、登录终端、登录来源 IP、登录时间、退出时间等。具体字段定义在 Linux 源码的struct utmp结构里,登录时写入一条,注销时再写一条,系统重启和关机也会生成对应记录。
正因为它是二进制格式,直接读取会看到乱码,所以才需要 last 这类工具解析。换个角度看,这其实是个优点:文本日志容易被误编辑和篡改,wtmp 的结构反而让手工改动更麻烦。但麻烦不代表不可能,如果有人拿到 root 权限,依然可以清空或伪造这个文件,所以 last 的输出只能作为排查线索,不能当作司法级别的证据。
还有一个容易踩的坑:某些最小化安装的 Linux 系统默认没有/var/log/wtmp。如果执行last提示文件不存在,通常会伴随“wtmp begins 1970”之类的输出,这是因为系统还没有记录过任何登录事件。遇到这种机器不用慌,可以用touch /var/log/wtmp创建文件,也可以安装sysstat等工具包时自动补齐。
2. 核心场景与选项选型:拿到 last 输出之后怎么看
2.1 典型使用场景:巡检、追踪、取证、审计
last 命令最常见的应用场景有这么几类。
第一类是日常巡检。登录一台主机后,快速查看最近的登录记录,确认没有陌生 IP 或陌生账号出现过。这个场景我习惯用last -n 10 -i,-n 限制条数,-i 把 IP 从域名反查结果转回原始数字格式,避免 DNS 解析慢导致命令卡住。
第二类是异常登录追踪。当你怀疑服务器被入侵或账号被共享时,last 能帮你拉出某个用户在特定时间段的所有登录历史,再配合lastb查看失败登录记录。安全审计里经常要求“还原攻击路径”,last 的输出就是还原过程的第一块拼图。
第三类是重启历史核查。系统无故重启,或者运行时长异常时,用last reboot能列出每次重启的时间。结合内核日志里的启动时间,基本能判断重启是人为执行还是意外宕机。
第四类是审计合规。等保、ISO 27001 等检查项通常要求记录并留存登录日志,last 配合日志轮转、集中采集(比如 rsyslog 转发或 ELK 采集),能形成一份可追溯的登录行为时间线。
2.2 常用选项对照与选型建议
last 的选项不算多,但每个选项解决一个特定问题,我把高频选项整理成了一张表,方便现场翻阅。
| 选项 | 作用 | 使用建议 |
|---|---|---|
-n 数字 | 只显示最近 N 条记录 | 巡检时用-n 10,避免输出刷屏 |
-i | 显示点分十进制 IP,不做 DNS 反查 | 服务器无内网 DNS 或反查慢时必须加 |
-f 路径 | 指定其他 wtmp 文件 | 排查轮转后的旧日志wtmp.1时很常用 |
-x | 显示系统关机、重启、运行级别变更记录 | 配合reboot、shutdown参数看系统生命周期 |
-R | 不显示主机名和 IP 字段 | 只关心用户维度时可以让输出更干净 |
-a | 将主机名/IP 显示在最后一列 | 方便脚本按列切割,也方便人眼对齐 |
-d | 显示远程主机 IP,某些系统上配合 -a 使用 | 处理有代理或跳板机的登录场景 |
-t 时间 | 显示指定时间点之前的记录 | 时间格式YYYYMMDDHHMMSS,用于时间范围筛选 |
-p 时间段 | 显示指定时间段内的记录 | 比 -t 更直观,适合“查某一天”的需求 |
-F | 显示完整登录和退出时间 | 审计时需要精确到秒可以加 |
-w | 不把域名截断为短名称 | 有长域名环境建议开启 |
选型上我的习惯是:人性化查看用last -n 20 -i,排查异常用last -F -i -n 50,查轮转日志用last -f /var/log/wtmp.1。不要一上来就敲一个光秃秃的last,输出几百行只会让人眼晕,还容易错过真正有价值的异常记录。
2.3 last 与 who、w、lastb、lastlog 的分工配合
这几个命令经常被拎出来对比,我直接说结论。
who 查当前在线用户,数据来源是/var/run/utmp,是“此刻快照”;w 在 who 的基础上多了终端号、登录时长和当前执行的命令,是“实时监控”;last 查 wtmp,是“历史档案”;lastb 查 btmp,专记失败登录,是“攻击告警”;lastlog 查/var/log/lastlog,记录每个用户最近一次登录时间,是“全量用户概览”。
实际排查时,我通常是五连招:先 last 看历史,再 lastb 看爆破,然后 who 看当前在线,lastlog 看有没有用户首次登录,最后 w 看在线用户在干什么。这一套下来,一台机器的登录画像基本就清晰了。拿 last 单独使用没问题,但和这几个命令组合,才能覆盖完整链路。
3. 实操过程与核心环节实现:从示例到解读
3.1 最基础的 last 输出怎么看懂
直接执行last,输出大概是这样的:
$ last -n 10 -i root pts/0 192.168.1.20 Thu Dec 12 09:15 still logged in zhangsan pts/1 10.0.0.8 Thu Dec 12 08:42 - 08:43 (00:01) reboot system boot 4.18.0-477.el8.x86_64 Thu Dec 12 08:40 still running root pts/0 192.168.1.20 Wed Dec 11 22:10 - 22:30 (00:20) root pts/0 192.168.1.20 Wed Dec 11 09:02 - 09:22 (00:20) wtmp begins Wed Dec 11 08:30:01 2024每一行从左到右依次是:用户名、登录终端、登录来源(IP 或主机名)、登录开始时间、退出时间、会话时长。最后一行wtmp begins表示当前 wtmp 文件里的最早记录时间,注意这不代表系统第一次安装时间,而是这个日志文件开始记录的时间,日志轮转后这个时间会被重置。
几个特别值得注意的字段:still logged in表示该会话当前仍在线,对应到who就是还在系统里的用户;still running表示系统从那个时间点启动后一直没关过机;crash字样虽然不常见,但出现时代表系统没有正常关机(比如断电或内核崩溃)。crash在部分系统上显示为gone - no logout,含义一样。
终端名也藏着信息:tty开头表示本地终端登录(物理终端或串口),pts/N表示伪终端登录,通常来自 SSH、SSH 跳板或终端工具。看到pts但来源 IP 很奇怪时,就要重点排查。
3.2 用 -x 还原系统重启和关机时间线
last -x会把系统事件也输出出来,最典型的是reboot和shutdown记录:
$ last -x -n 15 -i reboot system boot 4.18.0-477.el8.x86_64 Thu Dec 12 08:40 still running shutdown system down Thu Dec 11 21:30 - 21:33 (00:02) reboot system boot 4.18.0-477.el8.x86_64 Thu Dec 11 20:10 still running这里能看到系统每次开机、关机的时间点。配合uptime看当前运行时长,配合/var/log/messages或journalctl看关机前后的系统日志,就能判断是计划内维护还是异常掉电。我自己遇到过一台机器每隔两三天就自动重启,last -x扫一遍发现每次重启前都有shutdown记录,再翻系统日志找到是一条定时任务执行的shutdown -r,问题当场定位,比瞎猜高效得多。
要注意的是,shutdown记录行里的登录时间和退出时间分别代表“开始关机”和“关机流程结束”,会话时长是关机过程耗时。这部分输出在不同发行版里字段对不齐也正常,别死抠格式。
3.3 用 -f 解析轮转后的历史日志
默认情况下,last 只读/var/log/wtmp。但生产环境大多配置了 logrotate,日志会按天或按大小轮转,生成wtmp.1、wtmp.2.gz之类的旧文件。查历史记录时,就要手动指定文件:
$ last -f /var/log/wtmp.1 -n 20 -i如果旧日志被压缩成了.gz,last -f直接读不行,先解压再读:
$ zcat /var/log/wtmp.2.gz > /tmp/wtmp.2 $ last -f /tmp/wtmp.2 -n 20 -i注意这会把解压后的文件放到 /tmp,敏感环境操作完记得删掉。生产环境更推荐的方式是把 wtmp 采集到日志中心,在日志中心里按关键字检索,而不是留一堆本地解压文件。
logrotate 的配置通常在/etc/logrotate.d/wtmp,默认策略可能是每个月轮转一次,保留一定份数。为了避免 wtmp 无限增长占满磁盘,这个配置不要随意删除。轮转文件的保留时长决定你能回溯多久的登录历史,建议至少保留 180 天,合规要求严格的可以保留更久。
3.4 按用户、按重启、按 IP 定向查询
last 支持将用户名或特殊关键字作为参数直接筛选。查某个用户:
$ last zhangsan -n 20 -i查所有重启记录:
$ last reboot查失败登录(需要 btmp 文件存在,且通常需要 root 权限):
$ sudo lastb -n 20 -i查某个 IP 的登录记录,这个没有直接参数,需要靠 grep 二次过滤:
$ last -n 200 -i | grep "192.168.1.20"在实际安全排查中,我常把 IP 筛选和 awk 结合,快速统计某个 IP 的登录次数:
$ last -n 500 -i | awk '$3 ~ /^192.168.1.20$/ {count++} END {print count}'这类组合写法看似基础,但在应急响应时非常实用,能在最短时间内回答“这个 IP 到底登过几次”这种审计问题。
3.5 输出重定向与脚本化处理
last 的输出可以重定向到文件,也可以交给管道做进一步处理。生成当前在线会话快照:
$ last -F -i > /tmp/login_history.txt用 cron 每天定时收集一份登录快照,保留一年,就是很多小型团队最朴素的登录审计方案:
0 2 * * * last -F -i > /var/log/login_audit/$(date +\%F).log 2>&1注意 cron 里%需要转义成\%,这个坑我踩过不止一次。写这种脚本时还要留个心眼:last 输出里第一行的wtmp begins末尾不带 IP 字段,如果脚本按第三列切 IP,要先排除掉这类行,或者用grep -v "^wtmp"过滤。
4. 常见问题与排查技巧实录
4.1 “last: /var/log/wtmp: No such file or directory”
这个问题我在刚装完最小化系统的 CentOS、Ubuntu 上都遇到过。原因很简单:系统里压根没有 wtmp 文件。解决办法就是创建空文件并设置好权限:
$ sudo touch /var/log/wtmp $ sudo chown root:utmp /var/log/wtmp $ sudo chmod 664 /var/log/wtmp不同发行版对 utmp 组名可能有差异,有的叫utmp,有的直接归root。创建完文件之后,等有人登录一次再试last,输出就从wtmp begins变正常了。没配置 sysstat 的最小化系统尤其容易触发这个问题,装完系统建议顺手把sysstat安装上,它会把 wtmp、btmp 的 logrotate 配置一并处理好。
4.2 last 输出变慢,卡住不动
按下last -n 10之后,终端卡了好几秒才出结果,这是最烦人的问题之一。主要原因在于 last 默认会对登录来源做 DNS 反查,把 IP 解析成主机名。在内网环境 DNS 配置不正确或反查超时时,命令就会卡在解析阶段。解决办法就是加-i,强制以点分十进制 IP 显示,不做反查。
如果必须显示主机名,可以检查/etc/hosts和/etc/resolv.conf,保证 DNS 配置正常。这里我再多提一句:生产环境中 last 查询尽量默认加-i,一方面省时间,另一方面很多审计系统只认原始 IP,不需要主机名。
4.3 wtmp 文件巨大,查询慢且磁盘告警
长时间不清理、logrotate 失效或异常写入,都会导致 wtmp 文件膨胀。我曾经在一台长时间运行的机器上见过几十 GB 的 wtmp,磁盘告警一查竟然是这个文件。处理办法分两步:先确认哪个文件占用空间:
$ sudo du -sh /var/log/wtmp*然后按照既定策略清理。保留最近一个月、归档更早日志是常见做法:
$ sudo logrotate -f /etc/logrotate.d/wtmp也可以手动清零,但清零前一定要先做好备份,因为这是审计数据:
$ sudo cp /var/log/wtmp /var/log/wtmp.bak $ sudo truncate -s 0 /var/log/wtmp有强制合规要求的场景,不要手动 truncate,优先采用集中采集方案,让日志实时上送后再做轮转,既保证可追溯又不占本地磁盘。
4.4 last 记录被清空,如何应对
恶意清理痕迹的第一个目标通常就是 wtmp 和 btmp。遇到last输出只剩几条,或者wtmp begins时间非常近,基本可以判断文件被动过手脚。此时立即把当前的 wtmp 文件做镜像备份:
$ sudo cp -a /var/log/wtmp /evidence/wtmp.bak $ sudo cp -a /var/log/btmp /evidence/btmp.bak然后检查是否有登录记录被定向到其他文件。部分攻击者会修改系统配置,让 SSH 或登录程序把日志写到自定义路径,需检查sshd_config的PAM相关配置以及/etc/rsyslog.conf里有没有奇怪的转发规则。还要注意一点:光看 last 不够,last 只覆盖登录会话,攻击者的命令执行痕迹要配合history、auditd、shell history 文件共同排查。
4.5 reboot 记录和系统实际启动时间对不上
这通常是宿主机和虚拟机时间不一致、RTC 时间配置异常导致的。当你发现last reboot显示的时间和uptime、date对不上时,先检查系统时区:
$ timedatectl再检查硬件时间:
$ sudo hwclock -r如果系统使用 UTC、硬件时间却是本地时间,或反过来,就会造成跨时区的偏差。解决方式推荐统一使用 UTC,服务器不同时区协作时这一条非常重要。
此外,某些云平台自带 guest agent 会周期性同步时间,大量登录记录上的时间偏移可能来自 NTP 同步跳变。排查时把 last 输出里的登录时间与 NTP 服务日志对照,能分清是人为问题还是时间同步问题。
5. 延伸实践:把 last 从“查看命令”变成“审计工具”
5.1 统计登录次数与去重分析
把 last 的输出交给 awk、sort、uniq 处理,能快速得出很多有价值的数据。统计每个用户登录次数:
$ last -i | awk '{print $1}' | sort | uniq -c | sort -nr这个命令会输出所有账号的登录次数,reboot、wtmp这类非用户名行也在里面,如果干扰视线,可以用grep -Evi "reboot|wtmp|shutdown"过滤。统计来源 IP 次数同理:
$ last -i -n 1000 | awk '{print $3}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | sort | uniq -c | sort -nr加了正则过滤,是为了排除空字段和wtmp begins这种非 IP 行。这套组合是我排查暴力破解来源时的常备武器。
5.2 检测非工作时间登录
配合date命令和 shell 脚本,还能找出凌晨等非常规时段的登录记录。简单思路是循环读取 last 输出的时间字段,提取小时,判断是否落在非工作时间段。比如查最近 100 条记录里凌晨 0 点到 5 点之间的登录:
$ last -i -n 100 -F | awk '$5 >= "00:00" && $5 <= "05:59" {print}'这里依赖-F让时间输出更完整。要注意不同系统 last 输出的时间列位置略有差异,写脚本前先跑一两条看列结构。非工作时间段登录未必是坏事,但要重点核查,尤其是 root 账号的深夜登录,基本都是高危告警。
5.3 与系统审计日志配合做闭环
last 适合回答“谁登录过”,但回答不了“登录后做了什么”。安全闭环操作中,我会把 last 输出与 SSH 日志、sudo 日志、auditd 记录关联起来。典型场景是:发现某个 IP 在凌晨登录,先用last -i | grep IP确认会话时间,再用journalctl -u sshd --since "2024-12-12 01:00" --until "2024-12-12 02:00"查看该时段的 SSH 认证日志,最后用ausearch查 auditd 里同一时段的命令执行记录,形成一条完整行为链。
这种多源关联的做法比单看 last 可靠得多,因为 wtmp 可被篡改,而内核审计日志和集中采集日志的篡改难度更高。生产环境里,我建议把 last 输出纳管到日志平台做长期保存,并配置关键账号登录告警。
5.4 监控新账号与异常登录的实战小脚本
这里分享一个我实际用过的巡检脚本片段,每天跑一次,检测当天新增的用户和当天 root 登录次数。新增用户检查用的是passwd文件比对,root 登录次数用 last 统计:
#!/bin/bash TODAY=$(date +%b%e) ROOT_LOGIN_COUNT=$(last -i -F | grep "^root " | grep "$TODAY" | wc -l) echo "[$(date '+%F %T')] root 今日登录次数: $ROOT_LOGIN_COUNT"这只是一个雏形。如果要发给告警系统,可以把结果转成 JSON 或直接调用 webhook。脚本的重要前提是服务器时间准确,否则统计口径会乱。我在实际部署时常遇到 cron 时区与业务时区不一致的坑,建议在脚本开头用export TZ=Asia/Shanghai之类的方式固定时区,管理多地域服务器时尤为重要。
5.5 数据可视化与长期留存建议
last 输出本质是结构化文本,非常适合导入 ClickHouse、Elasticsearch 或简单的 SQLite 做统计。最轻量方式是把 last 输出定期导入 SQLite:
$ last -F -i > /tmp/last_output.txt $ sqlite3 login.db <<EOF CREATE TABLE IF NOT EXISTS login_logs ( user TEXT, tty TEXT, ip TEXT, login_time TEXT, logout_time TEXT ); EOF然后用 SQL 就能做复杂查询,比如“每个 IP 的登录次数排名”“每个用户的平均会话时长”。这套方案比肉眼翻 last 输出可靠得多,也方便日后写审计报告。
中小型团队没有条件上 ELK 时,用last + SQLite + 定时脚本就能搭出一套登录审计雏形。注意定期备份数据库文件,并限制数据库文件的权限,因为里面包含敏感登录信息。
我在实际项目里还试过一种更轻量的方案:把 last 输出接入到 Prometheus 的 textfile collector,用 node_exporter 定时抓取登录次数指标,再配 Grafana 展示。效果相当直观,登录次数曲线异常上升时,一眼就能发现问题,而不用等人跑到机器上敲命令。
5.6 使用 last 时最容易忽略的三个细节
第一,非 root 用户执行 last 也能看到大部分登录记录,因为 wtmp 是 664 权限,普通用户具有读权限。所以在多用户服务器上,登录历史对全体用户可见。如果这一点违反公司内部信息隔离要求,需要调整权限或改用其他审计方案。
第二,last 记录的是登录会话,不一定等于实际的人。同一个人可能用不同账号、不同跳板机登录,也可能多个人共享同一账号。做审计时要结合组织账号管理规定,否则很容易误判。
第三,last 时间和当前时间不一致时,先查时区和 NTP,别急着怀疑系统被入侵。曾经有台机器所有用户登录时间都快了 8 小时,排查到最后是系统时区被改成了 UTC,虚惊一场。
我个人在实际操作中的体会是:last 命令学习成本低,但用好它需要对 wtmp 机制、日志轮转、系统时间体系有足够理解。想真正搭建可靠的登录审计能力,还要把 last 和 SSH 日志、sudo 日志、集中采集系统关联起来。遇到可疑记录多追问一句“这个时间段里系统还发生了什么”,往往能找到比表面登录痕迹更重要的线索。最后再分享一个小技巧:把alias last='last -i -n 30'写进/etc/profile.d/,对新手更友好,也能减少误判风险,但注意部分审计场景需要完整历史,别让 alias 挡住完整输出。