
不知道你第一次接触 Linux 服务器的时候有没有被用户登录方式这个问题绕晕过。前两天一个朋友跑来问我他在公司跳板机上登录应用服务器w命令显示他是pts/5但安全审计要求他证明这个终端确实来自办公网段他翻来覆去只找到last里的一行不知道怎么关联 IP、进程和会话类型。其实这类问题的完整答案藏在 wtmp 记录、实时会话信息、进程树和认证日志四个地方。这篇文章要讲的五招就是把这几处线索串起来last看历史、who/w看现场、loginctl看会话类型、进程树看来源、认证日志看证据。看懂之后你不仅能快速判断一个用户是 SSH 远程登录、本地控制台登录还是桌面图形登录还能顺手把来源 IP、登录时间和进程链路一起挖出来日常运维、安全排查和面试准备都用得上。1. 先搞清楚登录到底有哪几种不然判断无从下手很多人一上来就敲last看到一堆pts/0、tty1却不知道该怎么下结论。最根本的原因是脑中没有一个登录模型。在常见的 Linux 发行版里真正会写入登录记录的入口只有三类。1.1 三类常规登录入口远程网络登录最常见的就是 SSH客户端连过来之后由sshd分配一个伪终端在utmp和wtmp里表现为pts/N同时记录发起方的 IP 或主机名。本地控制台登录直接在裸机前按 CtrlAltF1F6 切换到的虚拟终端由login或getty引导在登录记录里表现为tty1tty6。这种登录通常没有来源 IP主机名位置是空白或者只显示本机。桌面图形登录通过 gdm、sddm、lightdm 这类显示管理器进桌面会话关联到seat0在登录记录里往往表现为:0或者:0.0。还有一类容易被误会成登录的东西su、sudo、cron 任务、systemd 服务。它们虽然会让某个用户的身份出现在进程里却不会写入传统的wtmp登录日志也不会在who里增加一个新的交互会话。后面我会专门说明怎么避免被它们带偏。1.2 tty 与 pts 的直观含义理解tty和pts这两个词判断就成了一半。tty 是 Teletype 的缩写沿用了老式电传打字机的叫法指物理或虚拟控制台终端pts 是伪终端从设备由 pty 对生成SSH、telnet、终端模拟器都属于这一类。可以这么记tty 更实体pts 更虚拟。但这不能反过来倒推——pts 不一定是远程登录因为你在本地图形桌面里打开一个 gnome-terminal它也是 pts。所以判断登录方式不能只看终端名称还要结合来源 IP、进程父链和日志类型。1.3 哪些情况不算登录举几个真实场景运维执行su - root之后who里不会突然多出一个 root 的会话因为当前终端还是原来那个用户登录时建立的UID 变了会话没有变。cron 每分钟跑一个脚本脚本里用 root 权限执行任务也不会出现在last里。systemd 服务通过User指定运行用户同样不会创建交互式登录会话。知道了这些边界再看下面五招就不会把进程里看见 root误判成root 登录了。2. 第一招last 命令把历史登录记录摊开看last是判断登录方式的第一个切入点。它读取/var/log/wtmp这个二进制文件记录了所有成功的登录、登出以及系统的启动和关机事件。为什么要从它开始因为它保存的是历史全量数据即使那个用户早就下线了只要日志没被轮转掉你依然能翻出他当年是从哪儿登录的。2.1 last 输出解读直接执行last会得到一个表格root tty1 Sat Jul 5 08:30 - 08:45 (00:15) alice pts/0 10.0.0.5 Sat Jul 5 09:00 still logged in bob pts/1 192.168.1.20 Sat Jul 5 07:20 - 07:50 (00:30) reboot system boot 5.15.0-91-generic Sat Jul 5 06:10 - 09:30 (03:20)每列的完整含义是用户名、终端tty 或 pts、来源主机/IP、登录时间、登出时间、在线时长。最后一列如果显示still logged in说明该会话此刻还活着如果显示crash说明系统在用户没有正常登出的情况下断了电或者崩了。判断登录方式的关键就在终端列和来源列的组合上tty1且来源为空基本就是本地控制台键盘登录。pts/0且来源是10.0.0.5一定是远程登录默认指 SSH。pts/0且来源是localhost有可能是本机 SSH 到本机也可能是本地终端模拟器建立的伪终端会话。:0或者来源是:0.0几乎可以断定是桌面图形登录。2.2 用 last -x 看系统层面的登录行为last -x会在记录里追加显示reboot、shutdown、runlevel这类系统事件。这在排查用户登录方式时有一个很实用的场景如果某个用户显示在线了很长时间而系统在这个时间段内重启过那么last里可能出现异常的crash标记。你结合last -x里的重启时间点就能判断那条登录记录是不是跨重启的残留记录避免误以为某个远程会话一直没断开。2.3 过滤单个用户和时间范围日常用得最多的是这三个变体last -F alice # 只看 alice 的完整时间戳记录 last -a # 把主机名/IP放到最后一列方便肉眼扫 last -f /var/log/wtmp # 显式指定日志文件-F会把 Jul 5 09:00 展开成完整带秒的 Sat Jul 5 09:00:30 2024审计时很有用。-a适合在终端比较窄的场景下使用来源 IP 会被排到最后一眼就能看出哪些是远程、哪些是本地。3. 第二招who/w 看实时现场再配合 loginctl 精准切分会话last看历史who和w看现场。它们读取的是/run/utmp里面只有当前在线的会话。遇到现在到底有没有人登录、他是从哪儿登录的这类问题先跑这两个命令比翻日志更直接。3.1 who 和 w 的信息怎么读who输出类似root tty1 2024-07-05 08:30 alice pts/0 2024-07-05 09:00 (10.0.0.5)这里root在tty1上没有来源 IP属于本地控制台登录alice在pts/0上来源是10.0.0.5属于 SSH 远程登录。w比who多输出负载、空闲时间、当前执行的命令能够顺便判断这个用户是不是真的在干活还是挂机发呆w输出里的FROM列和who的来源列作用相同。who -u则会把每个会话的 PID 也列出来这一信息是后面第三招看进程树的关键入口。3.2 loginctl 把远程、本地、图形界面分得清清楚楚如果你用的发行版启用了 systemd-logind现在的主流发行版基本都启用我强烈建议把loginctl加入你的标准操作流程。它能以一种更结构化的方式来区分会话类型。loginctl list-sessions输出示例SESSION UID USER SEAT TTY 1 1000 alice seat0 tty1 2 1000 alice pts/0seat0说明这个会话绑定到了物理座位通常是本地控制台或桌面SEAT列为空且出现pts/0一般是远程会话。进一步看某个会话的细节loginctl show-session 2重点看这几个字段Typetty表示这是一个终端会话Typex11或Typewayland表示桌面图形会话。Remoteyes且带有RemoteHost10.0.0.5说明是远程会话。Servicesshd代表这个会话由 SSH 服务创建Servicelogin代表本地login程序创建。这就是为什么我把loginctl单独算作半招它把who里需要靠猜测的成分去掉了直接把结论摆在字段里。3.3 先认出你自己who am i如果你想确认自己当前这个 shell 属于哪个登录会话可以用who am i或who -m。它会只输出当前用户的记录。遇到自己在多个会话里登录过、分不清哪个是哪个的情况这个命令加上tty命令就能快速定位tty告诉你当前 shell 挂在哪个终端名下who am i告诉你这个终端是谁在用、来源 IP 是多少。4. 第三招顺着进程树找到最高可信度的证据前两招看的是结果这一招看的是因果。进程树是我个人最信任的判断方式因为伪造一条 utmp 记录比较容易要伪造一连串真实进程的父子关系几乎不可能。4.1 为什么进程树最可靠一个 SSH 会话的进程链正常长这样systemd(1) ── sshd(1049) ── sshd(1100) ── bash(1101) ── ...一个本地控制台登录的进程链长这样systemd(1) ── getty(800) ── login(810) ── bash(811) ── ...而一个桌面图形登录后打开的终端父链大概率是这样systemd(1) ── gdm(900) ── gdm-session-worker(950) ── gnome-session(960) ── gnome-terminal(970) ── bash(971)看到sshd出现在父链里就说明这个会话一定是通过 SSH 建起来的来源再怎么样也跑不掉。看到login或getty开头说明是本地控制台。看到gdm或sddm说明是图形桌面。4.2 实操从用户 PID 反查父进程链先用who -u拿到目标用户的会话 PIDwho -u输出类似alice pts/0 2024-07-05 09:00 . 1100 (10.0.0.5)最后一列是来源 IP倒数第二列1100就是 alice 会话的 PID。然后查它的进程树pstree -sp 1100-s表示显示祖先链-p显示 PID输出像这样systemd(1)──sshd(1049)──sshd(1100)这就是一条铁证PID 1100 是 sshd 的子进程alice 是从 10.0.0.5 SSH 进来的。如果是本地登录父链会变成login(810)或systemd(1)──login(...)如果是图形桌面里的终端父链里能看到显示管理器的进程名。4.3 screen/tmux 对终端身份的干扰这里有一个很常见的坑。用户通过 SSH 登录之后进入 tmux 或 screen再开新窗口执行命令。此时如果用w去看新窗口的 pts 可能显示为pts/2、pts/3来源 IP 却已经丢了。但不要慌去看 tmux 服务进程本身它的父链仍然会指向最初那个 sshd。所以遇到pts 多个且来源列空白的情况别急着下结论把 tmux/screen 的 server 进程拉出来顺藤摸瓜总能找到真正的 SSH 入口。5. 第四招认证日志里的铁证进程树看完了最后能给你书面证据的就是认证日志。系统审计、事故回溯、安全检查都靠它。多数 Debian/Ubuntu 系统写/var/log/auth.logRHEL/CentOS 系统写/var/log/secure。两者的内容格式类似我在下面以 Debian 系的日志为例RHEL 系只需要把文件名换成secure即可。5.1 三种典型日志行对比SSH 登录成功日志长这样Jul 5 09:00:30 host sshd[1100]: Accepted publickey for alice from 10.0.0.5 port 51222 ssh2 Jul 5 09:00:30 host sshd[1100]: pam_unix(sshd:session): session opened for user alice by (uid0)本地控制台登录日志长这样Jul 5 08:30:01 host login[810]: pam_unix(login:session): session opened for user root by (uid0) Jul 5 08:30:01 host systemd-logind[750]: New session 1 of user root.桌面图形登录日志长这样Jul 5 07:00:00 host gdm-password[1000]: pam_unix(gdm-password:session): session opened for user alice by (uid0)注意看关键词sshd、login、gdm-password这三个进程名直接告诉你登录方式。你甚至可以通过日志里Accepted publickey、Accepted password判断用户是用密钥还是密码登录的这对安全审计非常有价值。5.2 用 journalctl 补齐缺失如果你的系统没有维护传统的 auth 日志文件或者日志已经被轮转过可以用 journald 查journalctl -u sshd --since today journalctl _UID$(id -u alice) --since 2 hours ago第一条专门列出 sshd 服务的日志第二条把某个用户 UID 相关的所有会话记录拉出来。遇到日志被清空的极端情况journald 往往是最后一道防线。5.3 别漏掉失败登录lastb判断登录方式不只看成功记录失败记录同样重要。/var/log/btmp保存失败的登录尝试lastb命令读取它。默认情况下普通用户没有权限读取需要 root。如果发现某个来源 IP 对多个用户名做暴力尝试日志里会连续出现多条Failed password记录配合lastb可以确认攻击者的来源和所使用的登录服务。这个操作不需要额外装工具系统自带的lastb就够了。6. 第五招综合诊断实战把五招串成一条线单独看每一招都是命令组合起来才叫能力。下面用一个真实场景演示怎么把五招串起来。6.1 一个真实的排查案例某天收到告警服务器上有一个可疑 root 会话。我的排查流程是who last -a | head -20 loginctl list-sessionswho显示 root 在pts/3来源 IP 是10.6.7.8。查last -a确认这个 pts/3 确实由10.6.7.8建立登录时间在 20 分钟前。再执行loginctl show-session看到Servicesshd、Remoteyes确认是远程会话。最后跑pstree -sp父链里出现sshd。于是结论确定这是一个来自内网某 IP 的 SSH 登录。如果到这里还没法确定可能就是非交互式进程或容器场景这时再翻认证日志看日志行里是sshd还是login还是gdm差不多就闭环了。6.2 一键诊断脚本把这五招集成成一个小函数平时排查会快很多。你在运维机器上可以保存成脚本function login-check() { echo who -a ; who -a echo last -a | head ; last -a | head -10 echo loginctl list-sessions ; loginctl list-sessions --no-legend echo process parent chain for p in $(who -u | awk NF6 {print $6} | grep -E ^[0-9]$); do pstree -sp $p done }注意who -u的 PID 列在第 6 个字段所以 awk 先取第 6 列再去掉非纯数字的噪音。这个脚本在大多数 bash 环境可以直接用不适合或者没装 pstree 的系统可以换成ps -o ppid,pid,cmd --no-headers手动回溯。6.3 常见误判场景与避坑总结最后把我踩过的一些坑列出来希望你别再走一遍。su和sudo不是登录who、last里不会新增会话判断身份时必须看进程树而不是登录记录。pts/N不一定来自远程本地桌面里的 gnome-terminal 也会占用 pts。请结合loginctl show-session里的Remote字段和进程树确认。lastlog只能看最后一次它显示的是每个用户的最近一次登录适合快速筛查但不适合还原完整登录方式因为中间历史会被覆盖。日志轮转导致记录缺失last默认只读当前 wtmp如果曾经的记录被轮转了就看不到。可结合journalctl长时间范围内查询。SSH 改了端口不要看到非 22 端口的连接就排除 SSH。确认服务端配置或用ss -tnp查看进程名sshd进程名才是关键。我个人实际排查时最常用的组合其实是先w看现场 再loginctl定性 最后按需追进程树这三步五招里剩下的是给复杂现场兜底的。只要你熟练了这套流程任何一家发行版上只要用的是主流 systemd 环境基本五分钟内都能给出一个让审计满意的结论。