在日常 Linux 运维里排查“谁在什么时候登录过这台机器”是一个高频需求而系统自带的last和lastlog往往是被低估的两条命令。它们不需要额外安装任何软件包几乎所有发行版开箱即用读取的是系统在用户登录、注销、重启这些动作发生时自动写入的二进制记账文件。也就是说只要机器启动过、有人登录过证据就已经落在磁盘上了你要做的只是把记录读出来。这两个命令解决的是完全不同层面的问题。last关心的是“时间轴”它按时间倒序把每一次登录会话、每一次关机重启、每一次运行级别切换都列出来lastlog关心的是“最后状态”它为系统里每一个用户维护一条记录告诉你这个账号最后一次登录是什么时间、从哪里登录的如果这个账号从建立之日起就没被使用过它会明确告诉你从未登录。前者适合做事件追溯后者适合做账号清点。本文会围绕几个实操方向展开先给一条命令速览表说明last、lastlog以及配套的lastb、who、w各自负责什么然后拆解命令背后的四个关键文件wtmp、btmp、lastlog、utmp讲清楚它们为什么是二进制、为什么普通人读不了、为什么有的机器上last一执行就是空白接着用大量可直接复制的命令演示时间过滤、IP 显示、完整时间戳、指定历史日志文件、只查某个用户等常见操作最后补上日志轮转配置、自动化巡检脚本、常见故障排查表和审计合规提醒。适合读这篇的包括需要做服务器安全巡检的运维人员、要在故障复盘时确认“谁在几点动了机器”的后端开发、准备考 RHCE 或做 Linux 基础教学的同学以及正在写堡垒机、审计系统或资产盘点脚本的工程师。所有命令都在标准 Linux 环境下通用不依赖特定发行版涉及文件路径的地方我会同时标注 Debian 系和 RHEL 系的差异。1. last 与 lastlog 核心能力速览开始敲命令之前先把几条容易混淆的工具放到一张表里对比。很多人第一次接触last时会问它和who、w有什么区别答案藏在“数据来源”和“时间范围”这两个维度上。who和w读取的是/run/utmp只反映当前还在线的会话机器一重启这个文件就被清空last读取的是/var/log/wtmp这是一个只追加不删除的历史账本重启也不会丢。理解这个区别后面的所有行为就都说得通了。能力项说明命令归属last、lastlog、lastb均来自util-linux包主流发行版默认预装数据来源last读/var/log/wtmplastb读/var/log/btmplastlog读/var/log/lastlogwho、w读/run/utmp文件格式前三者中 wtmp/btmp/utmp 为二进制 utmp 结构记录lastlog 为按 UID 索引的定长稀疏文件默认排序last与lastb均为时间倒序最新记录在最上面lastlog按 UID 升序权限要求普通用户可以执行但能否读到完整内容取决于文件权限btmp通常仅 root 可读时间过滤支持-t指定截止时间支持-s指定起始时间两者可组合成时间窗口输出控制支持-n限制条数、-F显示完整时间戳、-i显示 IP、-R隐藏主机名、-a末列显示主机特殊记录-x可额外显示关机、重启、运行级别切换等非登录事件历史文件支持-f指定轮转后的历史文件如/var/log/wtmp.1API 能力无非网络接口需通过 shell 脚本或解析工具二次封装批量能力本身不支持批量但输出为稳定文本格式极易被脚本消费典型场景登录审计、异常排查、账号清理、合规取证、资产使用率统计不适合场景容器内部多数容器不写 wtmp、已清理日志的追查、实时监控需要特别说明的一点是lastlog的“从未登录”判断和账号是否被禁用是两回事。一个账号可能被usermod -L锁定此时它仍然会保留最后一次历史登录记录而一个刚创建但还没派发出去的账号lastlog会显示**Never logged in**这恰恰是账号清理时最有价值的信号。把这两类账号混为一谈是很多巡检脚本误报的根源。另外要提醒的是这些命令都只能看到“本机记录的登录行为”无法覆盖 SSH 之外的认证路径。例如通过su切换用户、通过sudo提权、通过 Web 控制台登录行为轨迹会落在secure、auth.log或sudo日志里last是看不到的。做完整审计时必须把last、lastlog、journalctl、auditd几路信息交叉比对单靠一条命令下结论并不可靠。2. 适用场景与使用边界先说清楚这个工具组合“能干什么”。最常见的场景是故障复盘某台生产机在凌晨三点挂了你需要确认那个时间段有没有人登录、是不是有人手动改了配置。last -F -i -n 50能把最近 50 条会话的时间精确到秒、来源精确到 IP配合-x还能看到系统在故障前后的关机重启动作一条时间线就拼出来了。这比翻history靠谱得多因为history只记录当前 shell 的命令换个人、换个会话就断片了。第二个高频场景是账号治理。很多公司的服务器上堆积着几十个历史账号离职员工的、临时外包的、测试用的谁也不敢删。用lastlog拉一遍全量把“从未登录”和“最后登录在一年以前”的账号筛出来再结合chage -l看密码有效期就能形成一份可执行的清理清单。这个过程不需要登录每个账号也不会对业务造成任何干扰安全性很高。第三个场景是安全巡检。lastb专门统计失败登录如果你看到某个 IP 在短时间内连续几十条失败记录基本可以判定是密码爆破尝试。配合last -i看同一 IP 是否最终成功登录就能快速判断事件等级。这里要注意lastb依赖/var/log/btmp文件存在且有写入权限部分发行版默认不创建该文件需要手动touch并设置600权限否则lastb会直接报错而不是返回空结果。2.1 能力边界与合规提醒必须强调的是登录审计属于敏感操作。last输出里包含用户名、来源 IP、登录终端等个人信息在多用户或跨团队环境中这些数据只应在授权的运维、安全、合规场景下使用。企业内部做审计时应确保有明确的管理制度支撑审计结果仅在必要范围内流转不要随手把完整的登录记录贴到公开渠道或工单系统里。另一个边界是“证据时效性”。wtmp和btmp会随日志轮转被压缩、被清理很多发行版默认只保留四周左右。如果你在事件发生一个月后才去查很可能什么都查不到。因此对关键服务器建议单独配置轮转保留策略或者定期把last输出归档到独立的日志平台。指望系统默认配置长期保存登录历史是一个很常见的认知误区。最后是“不能作为唯一证据”。wtmp是普通文件root 用户可以改写甚至删除恶意入侵者清理痕迹的第一步往往就是动这些文件。因此真正的取证场景需要结合文件完整性校验、远程日志服务器、auditd审计规则等多重手段。last的定位是“日常运维的快速视图”不是“司法级取证工具”把它放在正确的位置上才不会在关键时刻误判。3. 环境准备与前置条件last系列命令的部署成本几乎为零但要让它输出有意义的结果需要先确认几件事。第一是命令是否存在第二是数据文件是否存在第三是当前用户有没有读取权限。这三步检查加起来不超过一分钟却能避免后面大量“命令执行了但没输出”的困惑。3.1 确认命令与软件包绝大多数发行版把last、lastlog、lastb打包在util-linux里该包是基础系统组件通常不会缺失。如果确实找不到可以用下面的命令确认包管理器的归属# Debian / Ubuntu 系 which last lastlog lastb dpkg -S $(which last) 2/dev/null # RHEL / CentOS / Rocky 系 which last lastlog lastb rpm -qf $(which last) 2/dev/null # 如果确实缺失再考虑安装 # Debian 系 sudo apt update sudo apt install -y util-linux # RHEL 系 sudo dnf install -y util-linux需要注意部分精简镜像比如某些 Alpine 或容器基础镜像使用的是busybox提供的简化版last参数支持不完整-F、-i、-x可能直接报错。判断方法是执行last --help观察输出里是否列出了完整参数列表。如果只有寥寥几行说明用的是精简实现建议安装完整版util-linux再继续。3.2 检查四个关键数据文件登录记录全部落在/var/log和/run目录下动手之前先看一眼它们的状态# 查看文件是否存在、大小、权限、最后修改时间 ls -lh /var/log/wtmp /var/log/btmp /var/log/lastlog /run/utmp # 更精细地看权限位wtmp 通常 664btmp 通常 600lastlog 通常 660 stat -c %n %a %U:%G %s bytes /var/log/wtmp /var/log/btmp /var/log/lastlog /run/utmp这里有两个典型现象值得记住。其一btmp文件在很多系统上默认不存在因为只有配置了 PAM 的pam_tally或faillock模块后才会写入此时lastb会提示lastb: /var/log/btmp: No such file or directory这不是报错故障而是从未记录过失败登录。其二lastlog文件的大小看起来可能非常夸张动辄几百 MB 甚至上 GB但它实际占用的磁盘块可能只有几十 KB因为它是按 UID 直接寻址的稀疏文件UID 越大文件逻辑体积越大。用du -h和ls -lh对比一下就能看出差别不要被ls的数字吓到。3.3 权限与提权说明普通用户执行last通常可以正常看到自己的和部分他人的登录记录因为wtmp权限一般是664 root:utmp。但lastb读取的btmp是600 root:root普通用户执行会得到空结果或权限错误提示这属于设计上的安全考量——失败登录记录可能暴露账号是否存在不适合所有人可见。同理lastlog在部分发行版上是660 root:utmp普通用户可能读不到完整内容。因此本文后续所有涉及lastb和全量lastlog的命令都默认以具备sudo权限的账号执行。如果你的环境不允许提权那就把范围收窄到last本身它已经能覆盖大部分日常排查需求。4. last 命令详解与实战用法last的输出格式是固定的六列从左到右依次是用户名、登录终端、来源主机或 IP、登录时间、注销时间、会话时长。如果会话还在进行中注销时间列会显示still logged in如果是系统启动记录用户名会显示为reboot、shutdown或runlevel。这个格式稳定了二十多年正因如此才特别适合被脚本解析。4.1 基础用法与输出解读最朴素的执行方式就是不带任何参数last输出通常会以wtmp begins ...结尾这一行非常关键它告诉你当前wtmp文件里最早的一条记录是什么时间。如果这行显示的时间就是今天说明日志刚刚被轮转过或者系统刚重装你不可能查到更早的历史。很多人抱怨“查不到上周的登录”问题往往就出在这一行。限制输出条数是日常使用频率最高的参数-n后面跟数字即可# 只看最近 10 条 last -n 10 # 等价写法-10 是老式简写仍然可用 last -10 # 只看某个用户用户名支持多个空格分隔 last root last alice bob指定用户查询这个能力经常被忽略但它在实际场景中价值很高。比如怀疑某个账号被盗用直接last 该账号看登录 IP 是否出现了从未见过的网段或者在做离职账号清理时用last 该账号确认最后一次使用时间再决定是禁用还是删除。4.2 时间过滤-t 与 -s 的组合用法last的时间过滤参数设计得比较“反直觉”-t表示“显示到这个时间为止的记录”也就是取一个时间点之前的所有记录-s表示“从这个时间开始显示”。两者组合起来才能构成一个时间段窗口。时间格式是YYYYMMDDHHMMSS可以省略后面的部分比如20240315表示当天零点。# 查看 2024 年 3 月 15 日当天及之前的全部记录 last -t 20240315 # 查看 2024 年 3 月 15 日之后含的记录 last -s 20240315 # 组合成时间窗口3 月 1 日到 3 月 15 日之间 last -s 20240301000000 -t 20240315235959 # 精确到分钟排查某个凌晨时段的登录行为 last -s 202403150200 -t 202403150400 -F -i这套组合在故障复盘中特别好用。举个典型流程某服务在 2024-03-15 02:10 报错你先用-s 202403150200 -t 202403150300 -x -F -i拉出这个时段的所有事件包括登录、注销、重启再看有没有人在这个窗口里执行过操作时间线立刻清晰。相比全量last再肉眼筛选效率提升非常明显。4.3 输出格式控制-F、-i、-R、-a默认输出里时间是被压缩过的比如Mon Mar 15 02:10没有年份也没有秒。做精确比对时这个精度完全不够-F参数可以把完整时间戳打出来包含年份、日期、时分秒部分版本还会带上时区信息。# 完整时间戳 last -F -n 20 # 来源显示为 IP 而不是主机名避免 DNS 反解失败导致卡顿或显示异常 last -i -n 20 # 隐藏主机名列只保留用户、终端、时间 last -R -n 20 # 把主机名放到最后一列方便 awk 取值 last -a -n 20-i这个参数要重点说一下。默认情况下last会尝试把来源 IP 反解成主机名如果 DNS 配置有问题或者网络不通命令会明显变慢甚至显示成乱七八糟的名字。在生产环境排查时建议固定加上-i直接看 IP 更可靠也更符合排查直觉。-R则是把主机名那一列整个去掉适合只关心“谁在什么时候登的”而不关心“从哪来”的场景输出更清爽。4.4 -x 扩展事件与 -f 读取历史文件-x是让last从“登录列表”升级为“系统事件列表”的关键参数。加上它之后关机、重启、运行级别切换、内核版本变更这些记录都会出现在输出里用户名列分别是shutdown、reboot、runlevel。# 查看登录 系统事件的完整时间线 last -x -F -n 40 # 只看关机和重启记录 last -x | grep -E ^(reboot|shutdown) # 只看运行级别切换 last -x | grep ^runlevel-f参数用来读取轮转后的历史文件这是很多人不知道的实用能力。wtmp被logrotate轮转后会变成wtmp.1、wtmp.2.gz等直接last是看不到的。如果文件未被压缩可以直接用-f指定# 读取轮转后的上一份 wtmp last -f /var/log/wtmp.1 -n 30 # 读取 btmp 的轮转文件需要 root sudo last -f /var/log/btmp.1 -n 30如果历史文件已经被 gzip 压缩last无法直接读取需要先解压到临时目录再查或者用后面章节介绍的utmpdump配合zcat来处理。这个限制在跨月排查时经常会碰到提前把保留策略调好能省不少事。4.5 组合实战拼一条完整的排查命令把上面的参数组合起来可以得到几条覆盖不同场景的“成品命令”建议直接存到自己的排查手册里# 场景一最近 50 条会话完整时间 IP隐藏冗余列 last -n 50 -F -i -R # 场景二某日凌晨窗口内的全部事件含关机重启 last -x -F -i -s 202403150000 -t 202403150600 # 场景三指定用户最近 30 次登录完整时间 last alice -n 30 -F -i # 场景四从历史轮转文件中查上个月的登录 last -f /var/log/wtmp.1 -F -i -n 100 # 场景五只统计登录次数按用户聚合脚本化思路 last -i -R | awk NF3 $1! {print $1} | sort | uniq -c | sort -rn | head -20最后一条虽然超出了last本身的范围但它代表了一个很重要的思路last的输出是高度结构化的六列以空格或制表符分隔用awk取第一列统计用户名、用grep过滤特定 IP、用sort | uniq -c做计数都非常方便。把这些拼在一条管道里就能快速回答“哪个账号登录最频繁”“哪个 IP 出现次数最多”这类问题比手工数要靠谱得多。5. lastb 与失败登录审计lastb的用法和last几乎完全一致参数通用唯一区别是数据来源换成了/var/log/btmp记录的是失败登录尝试。它的输出会在用户名位置显示尝试登录的账号名来源列显示发起方的 IP 或主机名时长列固定为00:00因为失败尝试不会产生会话时长。# 查看最近 20 次失败登录 sudo lastb -n 20 -F -i # 统计失败次数最多的账号 sudo lastb -i | awk NF3 {print $1} | sort | uniq -c | sort -rn | head # 统计失败次数最多的来源 IP sudo lastb -i | awk NF3 {print $3} | sort | uniq -c | sort -rn | head # 查看某个时间段的失败记录 sudo lastb -F -i -s 202403010000 -t 202403080000这里的价值在于“聚合”。单看一条失败记录说明不了什么用户输错密码是常事但如果同一个 IP 在一小时内产生了几百条失败记录账号名还在root、admin、test、oracle之间轮换那就是典型的字典爆破特征。把上面的聚合结果和last -i | grep 该IP交叉验证一下如果失败几百次之后出现了一条成功登录优先按账号被盗处理立刻改密码、查authorized_keys、看历史命令。5.1 btmp 文件缺失的处理前面提过btmp不是所有系统都默认创建。如果执行lastb时报No such file or directory可以先确认 PAM 配置再决定是否手动创建# 确认 faillock / pam_tally2 是否启用 grep -rE pam_(faillock|tally) /etc/pam.d/ 2/dev/null # 手动创建 btmp仅 root权限必须 600 sudo touch /var/log/btmp sudo chown root:root /var/log/btmp sudo chmod 600 /var/log/btmp需要提醒的是创建空文件之后lastb只会显示btmp begins ...这样的空记录不会凭空产生历史数据。失败登录从此开始被记录之前的尝试无法追溯。这也是为什么关键服务器应该在部署阶段就把这部分配置确认好而不是等出了事才想起查。此外如果系统用的是systemd-journald统一收集失败登录也可能只体现在journalctl -u sshd里需要两路都看。6. lastlog 命令详解与实战用法理解了last再看lastlog会发现两者的设计哲学完全不同。last是“事件流水账”记录每一次动作lastlog是“状态快照”每个用户只保留一行。这种差异来自底层文件结构/var/log/lastlog是一个按 UID 直接寻址的定长记录文件读取某个用户时直接seek到对应偏移量效率极高但也意味着它天生只能保存“最后一次”。6.1 基础输出与字段含义lastlog输出共四列用户名、登录终端、来源主机、最后登录时间。终端列显示pts/0、tty1这类伪终端或物理终端编号来源列对于本地登录会显示为空或:0对于 SSH 登录会显示 IP 或主机名时间列如果显示**Never logged in**说明这个账号自创建以来从未成功登录过。# 查看指定用户 lastlog -u alice lastlog -u alice -u bob # 查看 UID 大于等于 1000 的普通用户过滤掉系统账号 lastlog -u 1000 # 设置“最后登录时间”的显示格式为完整时间戳 lastlog -t 30-u参数支持用户名也支持 UID这个细节在脚本里很有用直接传-u 1000就能把 UID 1000 及以上的账号全部列出来天然过滤掉bin、daemon、nobody这些永不登录的系统账号输出干净很多。-t参数表示“只显示最近 N 天内有登录的账号”注意它是按天数过滤不是按具体时间点。6.2 用 lastlog 做账号清点账号清理是lastlog最有价值的落地场景完整的操作流程大致如下。先拉全量并过滤系统账号把结果按最后登录时间排序人工确认后再执行禁用或删除# 第一步导出 UID1000 的账号最后登录情况 sudo lastlog -u 1000 /tmp/lastlog_report.txt # 第二步筛出从未登录的账号 sudo lastlog | grep -i Never logged in # 第三步找出超过 90 天未登录的账号借 date 计算阈值后手工比对 date -d 90 days ago %a %b %e # 第四步对候选账号先锁定而不是直接删除 sudo usermod -L -s /sbin/nologin username # 第五步观察一段时间无异常后再删除 sudo userdel -r username这套流程里最关键的判断是“先锁后删”。lastlog显示从未登录可能有很多原因账号是刚建的还在走流程、账号是给某个自动化任务用的但任务还没上线、账号被禁用后lastlog记录被重置过。直接删除风险很高万一有服务依赖这个账号的 UID 或家目录删了就是事故。锁定账号既能立即止损又保留了回滚空间是更稳妥的工程做法。6.3 lastlog 文件的稀疏特性/var/log/lastlog有一个容易让人误解的特性它的文件大小等于“最大 UID × 每条记录长度”而不是“实际用户数 × 记录长度”。如果你的系统上出现过 UID 50000 甚至更大的账号常见于 LDAP 同步、容器映射、Kubernetes 场景文件逻辑大小会膨胀到几十 MB 甚至更多但du看到的实际占用可能只有几十 KB。# 对比逻辑大小和实际占用 ls -lh /var/log/lastlog du -h /var/log/lastlog # 查看文件系统的稀疏文件支持情况ext4/xfs 均支持 stat -f -c %T /var/log这个特性带来两个实践影响。第一备份lastlog时不要被大小吓到tar会自动处理稀疏文件实际传输量很小。第二如果你的监控系统按文件大小做告警lastlog会产生持续误报建议把它加入白名单或者改用du口径判断。理解了稀疏文件的机制这类困惑基本就消失了。7. wtmp/btmp/lastlog 文件格式与底层解析前面反复提到这些文件是二进制的那么它们到底长什么样wtmp和btmp使用同一套struct utmp结构每条记录定长字段包括记录类型、PID、终端名、用户名、来源主机、时间戳等。类型字段决定了这条记录是“用户登录”“用户注销”“系统启动”还是“运行级别切换”last就是靠这个字段区分reboot和普通用户的。最直接的查看工具是utmpdump它会把二进制记录转成可读文本# 把 wtmp 转成文本格式便于 grep 和比对 sudo utmpdump /var/log/wtmp | head -20 # 转 btmp sudo utmpdump /var/log/btmp | head -20 # 结合 grep 找特定用户的所有记录类型 sudo utmpdump /var/log/wtmp | grep aliceutmpdump的输出格式是方括号包裹的记录块字段顺序固定能清楚看到登录和注销是两条独立记录通过终端名和 PID 配对。这种原始视角在排查“登录记录缺失”时特别有用因为你能确认到底是文件里没有记录还是last的解析逻辑把它当成无效记录过滤掉了。如果需要在自己写的监控脚本里解析这些数据用 Python 读取也是常见做法。下面是按utmp结构解析wtmp的最小示例仅供学习结构之用生产环境建议直接用utmpdump或调用last的文本输出更稳妥import struct import datetime # struct utmp 在 64 位 Linux 上的常见布局字段顺序随内核版本略有差异 UTMP_FORMAT h2x32s4s4s4s4s2xh4s32s256shh4x UTMP_SIZE struct.calcsize(UTMP_FORMAT) def parse_wtmp(path/var/log/wtmp, limit10): rows [] with open(path, rb) as f: while len(rows) limit: data f.read(UTMP_SIZE) if len(data) UTMP_SIZE: break fields struct.unpack(UTMP_FORMAT, data) ut_type fields[0] user fields[1].split(b\x00)[0].decode(utf-8, ignore) line fields[2].split(b\x00)[0].decode(utf-8, ignore) host fields[3].split(b\x00)[0].decode(utf-8, ignore) ts fields[4] if ts 0: continue rows.append({ type: ut_type, user: user, tty: line, host: host, time: datetime.datetime.fromtimestamp(ts).isoformat(), }) return rows for row in parse_wtmp(limit5): print(row)这段代码要强调的是“仅供理解结构”因为不同发行版的struct utmp字段长度、填充字节、记录大小并不完全一致硬编码格式字符串很容易在跨平台时读到乱码。真要写工具正确做法是用ctypes绑定 libc 的getutent系列函数或者直接解析utmpdump的文本输出。这一点在实际项目里踩过坑的人应该深有体会。至于lastlog它的结构简单得多每条记录定长只包含终端、主机和时间戳三个字段按 UID 索引。因为格式简单用struct解析相对安全但在没有明确需求的情况下还是建议直接用命令输出。8. 日志轮转、保留策略与自动化巡检wtmp和btmp的保留时长完全由logrotate决定这是很多“查不到历史记录”问题的真正根源。大部分发行版的轮转配置里wtmp的保留份数是 1 到 4 周btmp通常保留 1 个月。如果公司有更长的审计要求就需要主动调整。8.1 查看与调整轮转配置# 查看默认轮转配置 cat /etc/logrotate.conf # 查看针对 wtmp/btmp 的具体规则不同发行版位置不同 grep -rn wtmp\|btmp /etc/logrotate.conf /etc/logrotate.d/ 2/dev/null典型的wtmp轮转规则长这样每月轮转一次、保留 1 份、创建新文件时权限0664 root utmp。如果要保留更长可以把rotate改成 12表示保留 12 份月度归档相当于一年。修改后建议用logrotate -d做一次 dry-run 验证语法# 只检查配置语法不实际执行 sudo logrotate -d /etc/logrotate.conf 21 | grep -A3 wtmp # 强制立即轮转一次谨慎操作会移动当前 wtmp sudo logrotate -f /etc/logrotate.conf第二类配置是btmp的权限。轮转规则里必须确保新文件仍是600 root:root否则普通用户就能读到失败登录记录存在信息泄露风险。这一点在自建规则时特别容易漏掉建议每次改完都用stat复核一遍。8.2 每日巡检脚本示例把last、lastb、lastlog组合成一个每日巡检脚本输出到独立目录是成本最低的审计增强手段。下面是一个可运行的骨架逻辑是抓取昨日登录记录、汇总失败登录 TOP、列出从未登录账号全部落到文件里供人工复核#!/usr/bin/env bash set -euo pipefail REPORT_DIR/var/log/login-audit DATE_TAG$(date %Y%m%d) mkdir -p $REPORT_DIR # 1. 昨日 00:00 到今日 00:00 的成功登录 YESTERDAY$(date -d yesterday %Y%m%d) TODAY$(date %Y%m%d) last -F -i -s ${YESTERDAY}000000 -t ${TODAY}000000 \ ${REPORT_DIR}/login-${DATE_TAG}.log 21 || true # 2. 同时间窗内的失败登录 TOP50 lastb -F -i -s ${YESTERDAY}000000 -t ${TODAY}000000 2/dev/null \ | awk NF3 {print $1, $3} | sort | uniq -c | sort -rn | head -50 \ ${REPORT_DIR}/failed-${DATE_TAG}.log || true # 3. 从未登录的普通账号 lastlog -u 1000 2/dev/null | grep -i Never logged in \ ${REPORT_DIR}/never-login-${DATE_TAG}.log || true # 4. 清理 90 天前的报告 find $REPORT_DIR -name *.log -mtime 90 -delete这个脚本有几个细节值得说明。set -euo pipefail保证任何一步出错都能被发现每条命令后面加|| true是为了避免某条命令返回非零导致整个脚本中断报告按日期命名并保留 90 天既满足基本追溯需求又不会把磁盘塞满。实际部署时可以挂到 cron 里每天凌晨跑一次。这类脚本的价值不在于多复杂而在于把“想起来才查”变成“每天都有记录”。9. 常见问题与排查方法这一节把实际使用中最常遇到的坑集中列出来配合命令直接给出排查路径。很多问题看似是命令坏了实际是数据文件缺失、权限不足或者轮转配置导致的历史被清空。问题现象可能原因排查方式解决方案last只输出wtmp begins ...一行wtmp 刚被轮转或系统刚重装ls -l /var/log/wtmp*看文件时间查轮转后的wtmp.1或用last -f指定历史文件last卡住十几秒才出结果DNS 反解超时加-i观察是否变快固定使用last -i或在/etc/hosts补齐解析lastb报文件不存在系统未创建 btmpls -l /var/log/btmp手动创建并设chmod 600 root:rootlastb输出为空但确实有失败登录普通用户无读权限id、stat /var/log/btmp用sudo执行lastlog显示大量**Never logged in**账号创建后未使用或 UID 复用结合chage -l、passwd -S看账号状态按流程锁定或删除先锁后删lastlog文件几百 MB稀疏文件按最大 UID 计算体积du -h对比ls -lh无需处理属正常现象监控口径改用 du用户登录了但last查不到使用su切换而非直接登录last -x、journalctl -u sshd交叉验证查/var/log/secure或auth.log容器内last无任何记录容器不写 wtmputmp 为空ls -l /run/utmp属预期行为容器审计走宿主机或日志平台历史文件是.gz无法-f读取logrotate 启用了压缩ls /var/log/wtmp*zcat解压到临时目录后再last -flast输出中文用户名乱码编码不一致locale查看当前字符集统一使用 UTF-8或用utmpdump原始查看脚本解析结果错位用户名或主机名含空格抽样检查原始输出用-R或-a固定列位置后再解析表格之外还有两个细节值得单独提。第一个是“用户名含空格”这个坑虽然少见但一旦出现所有基于awk按空格切列的脚本都会错位。稳妥做法是先确认环境里没有这种账号或者改用utmpdump的结构化输出解析。第二个是“时间跨时区”的问题如果服务器时钟是 UTC而你在东八区看记录会觉得时间对不上排查前先date确认一下系统时区。10. 最佳实践与安全审计建议把上面所有内容收敛成几条可以直接执行的建议供日常运维参考。第一把last -F -i作为默认写法而不是可选写法。完整时间戳和 IP 直显能消除大部分歧义多敲两个字符换来的是排查时不用二次确认。养成习惯之后回头看默认格式的输出反而会不适应。第二关键服务器务必调整wtmp和btmp的保留策略并在变更后做一次验证。默认四周的保留期对于事后审计来说太短尤其是发生过安全事件之后才发现日志已经轮转掉那种后悔是很难弥补的。调整时注意同步检查新文件的权限别在延长保留期的同时把权限放宽了。第三账号治理采用“先锁后删”的流程lastlog只是初筛工具不能作为唯一决策依据。一个账号从未登录可能是因为它服务于某个尚未上线的定时任务也可能是因为它绑定了某个服务的运行身份直接删掉家目录和 UID 会引发连锁问题。锁定、观察、再删除这个节奏看起来慢实际上省下的返工时间更多。第四不要指望单一数据源。last看登录lastlog看状态lastb看失败journalctl看服务层日志auditd看文件级操作每一样都有盲区。真正的安全审计是把几路信息按时间轴对齐后交叉印证任何一条孤立的记录都不足以定性。这一点在写自动化告警规则时尤其重要规则设计得越依赖单一指标误报和漏报就越多。第五涉及登录记录的所有操作都要放在合规框架内。登录信息包含账号、IP、时间等个人相关数据采集、存储、共享都应有明确的授权依据和保留期限。做巡检脚本时把输出目录权限收紧到750以内报告按周期清理不要把完整登录流水长期堆在公开可读的位置。最后补一句实操层面的提醒改完logrotate配置之后别只做 dry-run 就完事找一个低峰时段实际触发一次轮转确认新的wtmp能被正常创建、last能正常读取、历史文件能正常用-f打开。这一步验证花不了五分钟但能避免在真正需要查日志的那天才发现配置写错了。登录审计这套东西平时看着没用用上的时候就是关键证据值得提前把它收拾利索。
阅读完成 · 觉得有帮助?