修复 SSH Too many authentication failures 错误
SSH 报错“Too many authentication failures”通常是代理提供了超过服务器限制的密钥。使用 ssh -v 诊断,并通过 IdentityFile 与 IdentitiesOnly 固定正确密钥。
“Too many authentication failures”的含义
“Too many authentication failures”表示您的 SSH 客户端向服务器提供的密钥数量超过了服务器愿意检查的数量。服务器在尝试正确密钥之前就关闭了连接。这几乎总是客户端问题。密钥位于您的磁盘上,服务器已将其写入 authorized_keys,但这些事实都不起作用,因为连接过早结束。
具体过程如下。ssh-agent保存您已加载的所有私钥。客户端会逐个向服务器提供这些密钥,因为它无法知道该账户接受哪个密钥。服务器会拒绝不在 authorized_keys 中的密钥,并将每次拒绝都计为一次身份验证失败。sshd_config 中的 MaxAuthTries 限制了单个连接允许的失败次数。默认值为 6。如果您的代理中有 10 个密钥,而正确的密钥排在第 8 个,服务器会在尝试该密钥之前断开连接。
因此,解决方法是让客户端只提供一个密钥:正确的密钥。
服务器统计什么,以及 MaxAuthTries 的作用
公钥认证一开始就是一个尝试过程。客户端发送公钥,并询问服务器是否接受使用该密钥生成的签名。服务器回答是或否。“否”就是一次失败尝试,和密码错误完全相同。
sshd_config(5)手册页说明了此限制:“指定每个连接允许的最大认证尝试次数。当失败次数达到此值的一半后,后续失败会写入日志。默认值为 6。”
对于手动输入密码的用户来说,6 次尝试已经足够。但对于持有 10 个密钥的代理程序来说,这个次数并不多。当失败次数达到限制后,sshd 会断开连接,并在系统日志中写入类似以下内容的行:
error: maximum authentication attempts exceeded for deploy from 203.0.113.10 port 51292 ssh2客户端会输出同一事件的另一部分:
Received disconnect from 203.0.113.10 port 22:2: Too many authentication failures
Disconnected from 203.0.113.10 port 22这与 SSH permission denied(publickey)错误 是不同的失败。前一种情况下,服务器检查了你提供的所有内容,但一个都没有接受。这里则是服务器停止了检查。将这两种情况混为一谈,就会导致用户花费整个下午重新复制一个本来就正确的密钥。
为什么同一个密钥在同事的笔记本上可以使用
密钥和服务器都没有变化。对方的代理中有 2 个密钥,而你的代理中有 12 个。对方的密钥会先被发送,而你的密钥要到第 9 个才会发送;这时连接已经超时。
密钥数量会在不知不觉中增加。AddKeysToAgent yes 在 ~/.ssh/config 中会将你使用的每个密钥添加到代理,并一直保留。桌面密钥环代理(例如 Linux 上的 GNOME Keyring 或 macOS 的登录钥匙串)会在登录时自动加载密钥,而不会询问你。经过一年的时间,你可能添加了客户端密钥、Git 主机密钥和实验室服务器密钥。某一天,一台一直可以连接的服务器开始拒绝你的连接。服务器没有任何变化,只是你的代理中保存的密钥更多了。
如何使用 ssh -v 查看提供的密钥
使用 -v 重现失败的连接,并查看跟踪输出。
ssh -v deploy@203.0.113.10需要关注两类行。Will attempt key: 列出客户端已整理的身份,并按照实际使用顺序排列。每向服务器实际发送一个密钥,Offering public key: 就会出现一次。
debug1: Will attempt key: /home/you/.ssh/id_ed25519 ED25519 SHA256:AAAA... agent
debug1: Will attempt key: /home/you/.ssh/id_rsa RSA SHA256:BBBB... agent
debug1: Offering public key: /home/you/.ssh/id_ed25519 ED25519 SHA256:AAAA... agent
debug1: Authentications that can continue: publickey,password
debug1: Offering public key: /home/you/.ssh/id_rsa RSA SHA256:BBBB... agent您的路径、密钥类型和指纹可能不同。需要统计的是断开连接前出现的 Offering public key: 行数。如果这些提供的密钥依次发送,但会话结束前始终没有出现您指定的密钥,原因就已确定。行末的 agent 表示该身份来自 ssh-agent。explicit 表示该身份来自 IdentityFile 行,或来自命令行中的 -i。
然后查询 agent 当前持有的密钥:
ssh-add -l输出中的每一行表示一个已加载的密钥。如果输出 The agent has no identities.,说明问题不在 agent,应改为检查 ~/.ssh/config 中的 IdentityFile 行。如果输出 Could not open a connection to your authentication agent.,说明没有运行 agent,提供的密钥来自默认密钥文件。
修复方法 1:每台主机使用一个密钥并启用 IdentitiesOnly
IdentitiesOnly yes 告诉 ssh 只提供您配置的身份,并忽略 agent 主动提供的其他身份。将它与 IdentityFile 配置行配合使用后,客户端只会发送一次身份尝试。
Host vps
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519_vps
IdentitiesOnly yes将这些内容保存到 ~/.ssh/config,然后运行 chmod 600 ~/.ssh/config。如果文件可由组或所有用户写入,ssh 会拒绝运行,并显示 Bad owner or permissions on /home/you/.ssh/config。现在,ssh vps 只会提供一个密钥,而 ssh -v vps 应显示且仅显示一行 Offering public key:。
这里有两个容易令人困惑的细节。
IdentitiesOnly yes单独使用并不表示“一个密钥”。如果没有IdentityFile配置行,默认身份文件仍会被视为已配置的身份,因此 ssh 仍会尝试~/.ssh/id_ed25519、~/.ssh/id_rsa以及找到的其他默认文件。您还需要IdentityFile配置行:一旦设置该配置项,ssh 就不会再添加默认身份文件。- agent 仍负责签名。
IdentitiesOnly控制提供哪些密钥,而不是由谁执行签名。如果IdentityFile指定的私钥已加载到 agent 中,agent 会生成签名,您不会被要求输入口令。您甚至可以让IdentityFile指向匹配的.pub文件;当私钥仅存在于 agent 或硬件令牌中时,就应这样配置。
~/.ssh/config 中有一个陷阱会悄悄使此修复失效。大多数关键字采用首次找到的值,因此特定的 Host 块应放在 Host * 之前。IdentityFile 不遵循此规则。手册说明:“可以在配置文件中指定多个身份文件;所有这些身份都会按顺序尝试。”因此,Host * 下的 IdentityFile 会添加到每台主机的配置中,而不会替换它。遗留的全局配置行会再次将额外的身份尝试带回每个连接。
如果您希望设置全局安全保护措施,只在文件末尾设置该标志:
Host *
IdentitiesOnly yes之后每台主机都需要单独配置 IdentityFile,这也是您本来就需要的结果。为每台服务器指定一个密钥,还能在以后撤销某一台机器的访问权限,而无需重新签发所有密钥。应尽早养成这一习惯:参见 如何按机器管理 SSH 密钥。
修复 2:清理或重启代理
如果暂时无法编辑配置,请清空代理,只加载所需的密钥。
ssh-add -l # list what is loaded
ssh-add -d ~/.ssh/id_rsa # remove one key
ssh-add -D # remove every key
ssh-add ~/.ssh/id_ed25519_vps # load the one you need如果执行 ssh-add -D 后连接立即恢复,说明问题由代理导致。但应将此视为测试,而不是修复。桌面密钥环代理会在您下次登录时重新加载密钥,因此问题明天还会出现。~/.ssh/config 中的 IdentitiesOnly 行会在重启后保留。空代理不会保留。
您也可以为密钥设置有效期,让代理自动清除该密钥:
ssh-add -t 1800 ~/.ssh/id_ed25519_vps密钥添加后 1800 秒会被删除。重启代理同样有效,具体操作取决于启动代理的方式。您自行启动的 ssh-agent 可使用 ssh-agent -k 停止。如果您通过自行编写的 systemd 用户单元运行它,请使用 systemctl --user restart <unit> 重启该单元。密钥环代理会随桌面会话重启。
修复 3:仅连接一次的服务器使用一次性命令
对于不会添加到配置文件的主机,将相同设置直接放在命令行中:
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_vps deploy@203.0.113.10单独使用 -i 是最常见的错误修复方式。-i 会将一个密钥添加到身份列表中,但不会从该列表中移除 agent 的密钥。因此,其他密钥仍会先于你的密钥发送,连接仍会在达到限制时失败。不使用 IdentitiesOnly 运行 ssh -v -i ~/.ssh/id_ed25519_vps deploy@203.0.113.10 时,你会看到 agent 的密钥先被发送。-i 需要与 -o IdentitiesOnly=yes 一起使用。
要在一次连接中完全停用 agent:
ssh -o IdentityAgent=none -i ~/.ssh/id_ed25519_vps deploy@203.0.113.10此时,ssh 会直接从磁盘读取私钥;如果私钥设置了密码短语,ssh 会提示输入该密码短语。
基于 ssh 构建的工具也接受相同的选项:
scp -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_vps report.tar.gz deploy@203.0.113.10:/tmp/
rsync -av -e "ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_vps" ./site/ deploy@203.0.113.10:/srv/site/
GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_vps" git clone git@example.com:team/repo.git为什么错误会在第二跳出现
使用 ForwardAgent yes 时,agent socket 会在您连接到的服务器上可用。在该服务器上运行的 ssh 命令会通过转发的 socket 使用您本地的 agent,并可使用其中的全部密钥。因此,从跳转主机连接到最终服务器时可能出现错误,而第一跳运行正常。在中间机器上运行 echo $SSH_AUTH_SOCK:没有输出表示无法访问 agent。显示 socket 路径表示存在某个 agent,但不一定是您的 agent,因为在该机器上启动的 agent 也会设置相同的变量。在该机器上再运行 ssh-add -l:如果列出的是您本机上的密钥,则表示转发已启用。
agent 转发还会带来第二个风险。只要您的会话仍处于打开状态,中间机器上拥有 root 权限的任何人都可以使用您的 agent,以您的身份进行身份验证。ProxyJump 可以同时避免这两个问题:
ssh -J deploy@jump.example.com deploy@10.0.0.5ProxyJump 会通过跳转主机建立连接,并从您自己的机器向最终服务器进行身份验证,因此您本地的 ~/.ssh/config 会在每一跳生效,包括 IdentitiesOnly。在 加固 VPS 上的 SSH 时,关闭 ForwardAgent 是标准步骤。
是否应提高服务器上的 MaxAuthTries?
通常不应提高。先检查当前值:
sudo sshd -T | grep -i maxauthtriessshd -T 会输出生效的配置,包括默认值。因此,即使 sshd_config 中没有相关设置,它也会报告实际值。如果使用 Match 块,请添加 -C user=deploy,host=example.com,addr=203.0.113.10,因为这些块会按连接进行评估,否则会被跳过。
提高限制确实有效,但作用很有限:更大的数值会让行为异常的客户端获得更多尝试空间:
MaxAuthTries 20验证配置文件并重新加载服务。执行此操作时保留第二个会话处于打开状态:
sudo sshd -t
sudo systemctl reload ssh # Debian and Ubuntu
sudo systemctl reload sshd # RHEL family如果在 Ubuntu 24.04 上,systemctl is-enabled ssh.socket 报告 enabled,则表示 sshd 由 socket 激活。Ubuntu 的 ssh.socket 不会为每个连接启动新的守护进程。它会在第一个连接到达时启动 ssh.service,该服务随后继续运行并处理后续连接。上面的重新加载操作仍然是应用更改的方法。如果 ssh.service 尚未处于活动状态,则无需重新加载;下一个连接会使用新的配置文件启动该服务。
现在查看这项更改的实际效果。客户端正在提供服务器永远不会接受的密钥。提高上限后,服务器会为每个连接处理二十个被拒绝的密钥,而不是六个;这适用于所有连接的客户端,也适用于互联网上的每个密码猜测程序。每个密钥都会使服务器在 authorized_keys 中执行一次查找。您的登录速度仍然很慢,因为正确的密钥依然排在最后。向代理中添加第十三个密钥后,情况又会回到原点,您仍然需要再次请求提高数值。
只有在合法客户端确实需要提供多个身份时,才应提高该值。其他情况下都应修复客户端配置。当所有用户都使用已配置的密钥登录后,可以合理地降低该值以加强安全性,因为更小的数值会减少密码猜测程序在每个连接中的尝试次数。
为什么 fail2ban 会因此封禁您的地址
sshd 不会记录每个被拒绝的密钥。在默认的 LogLevel INFO 下,只有当连接的失败次数达到 MaxAuthTries 的一半时,针对有效账户的被拒绝密钥才会写入日志。这就是手册中“失败次数达到此值的一半后,后续失败才会被记录”的含义。默认值为 6 时,前 3 次被拒绝的密钥不会写入日志。最后 3 次会显示为类似下面的日志行,后面紧跟 maximum authentication attempts exceeded 行:
Failed publickey for deploy from 203.0.113.10 port 51292 ssh2: ED25519 SHA256:AAAA...这些行看起来像是一连串失败,但 fail2ban 的 sshd 过滤器不会逐行计数。其随附的默认配置 publickey = nofail 会读取有效用户的 Failed publickey 行,仅用于获取地址和用户名。真正用于计数的是 maximum authentication attempts exceeded,因此触及限制的一次连接只会增加 1 次失败,而不是多次。只要在 findtime 内出现 maxretry 次失败,监狱就会封禁源地址;fail2ban 自身的 jail.conf 会将这两个值设置为 5 分钟和 10 分钟。在 10 分钟内失败约 5 次就足以封禁您自己的地址,而在循环中重试的脚本或工具很快就会达到这个次数。
此时症状会发生变化,这也是最容易让人困惑的地方。您不再看到 “Too many authentication failures”,而是什么都看不到:连接一直挂起,最终超时,因为防火墙现在丢弃您的数据包,而不是返回响应。原本会收到错误消息、现在却变成超时,这就是判断信号。SSH 连接被拒绝与连接超时对此有详细说明。
通过服务提供商的控制台,或使用其他地址,检查监狱并解除封禁:
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 203.0.113.10在排查客户端期间,将您自己的地址填入 jail.local 中的 ignoreip;完成后再将其删除。监狱本身的配置方法请参阅 Ubuntu 24.04 的 fail2ban 指南。
一次性处理,避免问题反复出现
在 ~/.ssh/config 中为每台服务器配置独立的 Host 块,并设置 HostName、User、IdentityFile 和 IdentitiesOnly yes。完成后,ssh vps 命令很短,输入方便,只提供一个密钥,即使 agent 中的密钥很多,也不会触发 MaxAuthTries。这样,在其他故障发生时,ssh -v 的输出仍然足够简短,便于阅读。
FAQ
如何立即修复“认证失败次数过多”?
不要一次提供所有密钥,只提供一个密钥。要立即建立连接,请运行 ssh -o IdentitiesOnly=yes -i ~/.ssh/your_key user@host。要永久修复,请将一个代码块添加到 ~/.ssh/config,其中使用 HostName、User、IdentityFile 指定该密钥,并设置 IdentitiesOnly yes,然后运行 chmod 600 ~/.ssh/config。使用 ssh -v 确认:对于该主机,您应看到一行 Offering public key:。
为什么使用 ssh -i 仍会提供其他密钥?
因为 -i 只是将一个身份添加到列表中,并不会限制该列表。加载到 ssh-agent 中的密钥仍保留在列表中,并且仍会被提供,通常还会排在您的密钥之前。因此,在轮到您的密钥之前,服务器仍可能先达到 MaxAuthTries。-o IdentitiesOnly=yes 才是用于将 ssh 限制为指定身份的选项。请同时使用 -i 和 -o IdentitiesOnly=yes;对于单次连接,也可以使用 -o IdentityAgent=none 完全忽略代理。
是否应增大服务器上的 MaxAuthTries 来修复此问题?
不应这样做,几乎所有情况下都不应这样做。客户端正在发送服务器永远不会接受的密钥。增大该限制只会让服务器在每次连接中评估更多被拒绝的密钥,对于所有客户端以及到达服务器的每次暴力破解尝试都如此。只要代理中再加载一个密钥,问题就会再次出现。如果您想确认当前值,请使用 sudo sshd -T | grep -i maxauthtries 查看生效配置,然后使用 IdentitiesOnly 修复客户端。
为什么上个月运行正常的服务器现在开始出现此问题?
因为您的代理中加载了更多密钥。AddKeysToAgent yes 中的 ~/.ssh/config 会保留您使用过的每个密钥,桌面密钥环代理还会在登录时自动加载密钥。当已加载密钥的数量超过服务器的 MaxAuthTries 时,密钥在提供顺序中靠后的任何服务器都会开始连接失败。运行 ssh-add -l,并将密钥数量与服务器上的限制进行比较。
这会导致 fail2ban 封禁我的 IP 地址吗?
会,但速度比服务器日志显示的更慢。默认的 fail2ban sshd 过滤器不会统计有效用户的 Failed publickey 行。它会统计 maximum authentication attempts exceeded 行,因此每次达到该限制的连接都算作一次失败。使用发布时的默认值 maxretry = 5 和 findtime = 10m 时,大约 5 次此类连接就会在 10 分钟内封禁您的地址。明显的迹象是,错误会变成连接挂起,随后发生超时,因为数据包被丢弃,而不是收到响应。使用 sudo fail2ban-client set sshd unbanip <your address> 从控制台解除封禁,然后修复客户端再重新连接。