读懂 SSH 认证日志:每一行失败记录的含义
各发行版把失败的 SSH 登录记在哪里、如何解读不同的消息形态,以及如何区分打错密码的用户和正在跑清单的僵尸网络。
日志到底在哪里
这是第一个绊人的地方,因为它取决于发行版。Debian 和 Ubuntu 把认证事件写进 /var/log/auth.log。RHEL、CentOS、AlmaLinux、Rocky 和 Fedora 用 /var/log/secure。完全转向 journald 的系统可能这两个文件都没有,一切都在日志系统里。
可移植的答案是 journalctl,只要有 systemd 就能用:
# 可移植:实时跟读 sshd 自己的日志
journalctl -u ssh -f # Debian/Ubuntu 的单元名
journalctl -u sshd -f # RHEL/Fedora 的单元名
# 只看最近 24 小时的失败
journalctl -u ssh --since -24h | grep -E 'Failed|Invalid user'
# 在仍然写文件的发行版上:
grep -E 'Failed|Invalid user' /var/log/auth.log # Debian/Ubuntu
grep -E 'Failed|Invalid user' /var/log/secure # RHEL 系消息形态逐一解读
sshd 发出的失败消息只有一小组,每一种都有确切含义。记住下面这五种,几乎覆盖您会看到的一切:
- Failed password for <user> from <ip> port <n> ssh2 —— 账户存在,密码错误。要么是真实用户输错了,要么是机器人猜中了一个有效用户名。
- Failed password for invalid user <user> from <ip> —— 账户不存在。几乎总是攻击:您自己的用户知道自己的用户名。通常还会伴随一条单独的「Invalid user」记录。
- Connection closed by authenticating user <user> <ip> port <n> [preauth] —— 客户端在认证中途放弃。常见于只想抓取 banner 的扫描器,以及拒绝密码尝试的纯密钥服务器。
- Disconnected from authenticating user root <ip> port <n> [preauth] —— 在禁止 root 的服务器上发生的 root 登录尝试。这里量大是暴露主机上最常见的特征。
- Maximum authentication attempts exceeded for <user> from <ip> —— 客户端在一次连接内触达了 MaxAuthTries。往往是提供了很多密钥的 agent,但若来自陌生地址,那就是在轮换凭据的机器人。
区分攻击与手误
有三个信号可以区分它们,在自动处置之前,三个都该看到。
速率与持续性。人会在一分钟内尝试两三次,然后要么进去了,要么打电话给您。机器人会连续数小时保持稳定速率,通宵不歇,从不停顿。
账户多样性。人只会尝试一个账户:自己的。机器人顺着清单走,而其中大多数账户并不存在——正是上面提到的「invalid user」特征。
来源。您的同事从少数几个已知地址连入。攻击来自托管商网段,每几百次尝试就换一个地址,而且常常在同一小时内来自多个国家。
下面这条命令按来源地址对最近一天的失败记录分组,并显示每个地址尝试过哪些用户名,三个信号一次可见:
journalctl -u ssh --since -24h --no-pager |
grep -oE 'Failed password for (invalid user )?[^ ]+ from [0-9.]+' |
awk '{ ip = $NF; user = $(NF-2); count[ip]++; users[ip] = users[ip] " " user }
END { for (i in count) printf "%6d %-16s %s\n", count[i], i, users[i] }' |
sort -rn | head -15真正该告警的那一行
登录失败是噪音。有一条消息不是,而且它值得一条告警,而不是一块仪表盘:
Accepted password for <user> from <ip> —— 一次成功的密码登录。如果您的服务器本应只用密钥,这行就意味着它并不是,您有配置问题。如果允许密码,那么在同一网段的一串失败之后、从陌生地址出现的这一行,就是您最不想看到的序列。
值得一并关注的还有:带着您不认识的密钥指纹的 Accepted publickey,以及任何本不该有 sudo 权限的用户开启的 sudo 会话。这两者告警成本都很低,而重要性远超拒绝记录的数量。
# 最近一周所有成功登录,含方式与来源
journalctl -u ssh --since -7d --no-pager |
grep -E 'Accepted (password|publickey|keyboard-interactive)'从读取到阻断
手工读日志是诊断,不是防御——等您跑这条命令的时候,攻击已经持续一周了。真正能改变什么的,是持续不断地做这件事,并把阈值转化为防火墙规则。
最朴素的做法是跟读日志、按来源统计失败次数,并调用 nft 把地址加入阻断集合。它能跑起来,然后就撞上每个这类脚本都会撞上的问题:集合无限膨胀;高负载下日志轮转,事件丢失;升级之后单元悄无声息地死掉;一次误匹配把管理员挡在唯一入口之外。
SSH Protector 把这个循环作为服务来运行:一个合并的 nftables 集合、永远优先于封禁的白名单、开机时恢复的封禁,以及在本地做出的判断——因此网络扛不住的时候它仍然扛得住。它读取的正是上面展示的那条流,没有任何东西对您隐藏。
FAQ
- Linux 上失败的 SSH 登录记录在哪里?
- 在 Debian 和 Ubuntu 上是 /var/log/auth.log。在 RHEL、CentOS、AlmaLinux、Rocky 和 Fedora 上是 /var/log/secure。在完全依赖 journald 的系统上这两个文件都不存在,改为读取日志系统:journalctl -u ssh(或 -u sshd,取决于发行版的单元名),这在任何地方都适用。
- 「Failed password for invalid user」是什么意思?
- 表示有人尝试用一个本机上并不存在的账户进行认证。您自己的用户知道自己的用户名,所以这行几乎总是攻击——机器人在跑 root、admin、ubuntu、test、git、oracle 之类的常见名字清单。单看数量并不值得惊慌:那是任何暴露主机上的背景水平。
- 为什么我会看到数千次 root 登录尝试?
- 因为 root 是所有攻击清单上的第一个用户名,而机器人无从得知它在您的服务器上被禁止。把 PermitRootLogin 设为 no 或 prohibit-password 后它们无法成功,但也永不停止,因为一次尝试对它们毫无成本。要降低这个量,靠的是封禁来源,而不是登录策略。
- 怎么查看成功的 SSH 登录?
- 在同一份日志里搜索「Accepted」——Accepted publickey、Accepted password 或 Accepted keyboard-interactive,后面跟着用户名、来源地址和端口。这才是值得告警的那一行:在本应只用密钥的服务器上出现成功的密码登录是配置错误,而在一串失败之后来自陌生地址的成功登录则是安全事件。
