termal.in

← 博客

13 个常见 SSH 报错及逐一修复方法

· 更新于 2026年9月29日 · Termalin Team sshtroubleshootingtutorial

SSH 的报错信息是有意简短的——协议不会告诉一个陌生人它为什么把人拒之门外。那是好的安全性、糟糕的易用性:你看到的信息,往往离真正的问题隔着两步。这是一份实战手册,覆盖你真正会碰到的十三个报错、每一个到底意味着什么,以及通往修复的最快路线。

开始之前先交代一个工具:ssh -v user@host(最多可加到 -vvv——读懂那段输出(英文))。下面几乎每一个谜题,一旦你看清它死在握手的哪一步,就不再是谜题了。

报错一览

报错含义第一步
Connection refused主机应答了;但那个端口上没有东西在监听检查 sshd 是否在运行、是否在你拨的端口上
Connection timed out数据包在路上消失了检查云防火墙 / 安全组
Permission denied (publickey)你提供的密钥一个都没被接受先核对用户,再核对密钥,再核对 authorized_keys
Permission denied(没有提示)密码认证已关闭;必须用密钥用密钥,或通过服务商控制台登进去
Host key verification failed自你信任以来,服务器的密钥变了走带外渠道核实,再 ssh-keygen -R
Too many authentication failures代理提供了太多密钥用 -o IdentitiesOnly=yes 配合 -i
UNPROTECTED PRIVATE KEY FILE密钥文件别人也能读给私钥执行 chmod 600
Broken pipe一个空闲连接被断开了加上 ServerAliveInterval
no matching key exchange method老旧服务器,现代客户端只为那一台主机重新启用一个算法
Connection reset by peer在 SSH 打招呼之前 TCP 就被猛地关上检查 fail2ban / MaxStartups
Bad configuration option你的客户端太旧,读不了某一行配置升级 ssh,或给那一行加保护
did not receive identification string有东西连上了,却没发横幅通常是扫描器;在服务器端忽略即可
Unspecified GSS failure一次 Kerberos 尝试失败后回退了如果登录仍然成功,忽略它

1. Connection refused

机器是能到达的,但你敲的那个端口上没有东西在监听。这一条几乎从来不是你的错:

  • sshd 没在运行——从控制台执行 systemctl status sshd(VPS 服务商正是为这种时刻准备了网页控制台);
  • 它监听在一个非标准端口上——试试 ssh -p 2222,或检查 /etc/ssh/sshd_config 里的 Port;
  • 一道防火墙在主动拒绝(而不是丢弃)该端口。

如果你刚刚重建了服务器,那就是防火墙。如果它昨天还好好的,那就是 sshd 崩了或端口变了。

完整修复:connection refused 与 timed out 解码。

2. Connection timed out

和 refused 不同:你的数据包正在消失。主机宕了、IP 写错了,或者——在云主机上最常见——一个安全组 / 网络 ACL 悄悄丢掉了 22 端口的流量。先查服务商的防火墙规则,再看你是不是在一个封禁出站 22 的网络上(酒店和办公室的访客 Wi-Fi 特别爱这么干;试试基于 443 的访问方式,或换个网络来确认)。

**完整修复:**同一篇 refused 与 timed out 的逐步讲解里,有 timed-out 这一分支的分步内容。

3. Permission denied (publickey)

历届冠军。服务器只接受密钥认证,而它没有接受你提供的任何一把密钥。按可能性从高到低:

  • **用错了用户。**Ubuntu 镜像上是 ubuntu,Amazon Linux 上是 ec2-user,大多数裸 VPS 上是 root。同一把密钥配错用户名,产生的正是这个报错——云镜像各自挑了自己的登录用户,这占了首次登录 EC2/VPS(英文)一半的内容。
  • 你的密钥没被提供出去。ssh -v 会显示试过的每一把密钥。如果你的密钥不在列表里,就明确指向它:ssh -i ~/.ssh/id_ed25519 user@host。
  • **密钥不在服务器上。**它的公钥那一半,必须作为那个用户(不是 root)的 ~user/.ssh/authorized_keys 里的一行。
  • **服务器端的权限。**如果家目录、~/.ssh(700)或文件本身(600)是组可写的,sshd 会拒绝认可 authorized_keys。这就是经典的“我把密钥复制过去了,可还是不行”。

完整修复:Permission denied (publickey),一套快速而系统的修法(英文)会按顺序排查每一个元凶。

4. 你本以为会看到密码提示,却得到 Permission denied

如果你想用密码认证却从没被问过,那是服务器设了 PasswordAuthentication no——大多数云镜像出厂就是这样,而且这是正确的默认值。用服务商的控制台或你已有的密钥登进去,然后把新密钥正经地加上,而不是把密码重新打开。

5. Host key verification failed / REMOTE HOST IDENTIFICATION HAS CHANGED

带着 ASCII 骷髅气场的吓人横幅。服务器出示的密钥,和你之前信任的那一把不同。两种解释:机器被合法地重建/重装了(绝大多数情况是这个),或者你和服务器之间有东西在冒充它(这个警告正是为之而存在的那种情况)。

当且仅当你确知服务器是被重建了:ssh-keygen -R hostname 会删掉那条过期的记录,下次连接就会重新信任。别不假思索地把那条删除脚本化进你的工作流里;那个警告为真的那一次,它是你能得到的唯一一次警告。

完整修复:Host key verification failed,以及修复它的正确方式(英文)。

6. Too many authentication failures

你的代理热情地提供了六把密钥,服务器的 MaxAuthTries 说够了。让客户端只提供你想用的那一把:

ssh -o IdentitiesOnly=yes -i ~/.ssh/the_right_key user@host

……或者在 ~/.ssh/config 里按主机把它写死。如果你带着很多密钥,在 Host * 下写一个 IdentitiesOnly yes,能让你永远免除这个报错。

7. WARNING: UNPROTECTED PRIVATE KEY FILE

你的私钥别人也能读,于是客户端拒绝使用它。chmod 600 ~/.ssh/id_ed25519。在 Windows 上,这会在机器之间或从云盘里复制密钥之后出现——通过文件的“安全”属性来修,或者干脆别把密钥放进会同步的文件夹里。

8. client_loop: send disconnect: Broken pipe(你一移开视线就死掉的那个会话)

空闲连接被你和服务器之间的某个 NAT 路由器或激进的防火墙丢弃了。保活可以在客户端这边修好它:

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 4

容忍两分钟的沉默,每 30 秒发一个包,这样 NAT 表就永远不会把你忘掉。如果会话是在负载下、而不是空闲时死掉,那是另一种生物——去看 MTU(尤其是 VPN),而不是保活。

9. no matching key exchange method found / no matching cipher

你在和某个老东西对话——一台路由器、一台交换机、一台卡在老旧加密上的设备。现代 OpenSSH 出于充分的理由移除了那些算法。只为那一台主机重新启用它们,绝不要全局启用:

Host ancient-switch
    KexAlgorithms +diffie-hellman-group14-sha1
    Ciphers +aes256-cbc

报错信息会点出服务器提供的算法;把它列出的里面最不糟糕的那个加上。

10. ssh_exchange_identification: read: Connection reset by peer

TCP 连接刚打开就被猛地关上了,在 SSH 还没来得及打招呼之前。常见嫌疑:fail2ban 或 DenyHosts 封了你的 IP(从控制台查 /etc/hosts.deny 和 fail2ban 的 jail)、sshd 过载(MaxStartups 被一个猛敲端口的扫描器耗尽),或者一个负载均衡器的健康检查在吃掉连接。如果是 fail2ban、而且是你自己的服务器,修法是从控制台登录并 fail2ban-client unban <your-ip>——然后把 SSH 从扫描器的雷达上挪开,或者收紧 jail,免得你成了下周的误伤。

11. Bad configuration option: pubkeyacceptedalgorithms

不是服务器的问题——是你本地的 ssh 对你的配置文件来说太旧了。PubkeyAcceptedAlgorithms 是 OpenSSH 8.5 才有的(它取代了 PubkeyAcceptedKeyTypes);把这一行喂给一个更旧的客户端——某个年头的系统自带 macOS ssh、一个老发行版、一个极简容器镜像——它会干脆拒绝解析整个文件。三条出路:升级客户端(ssh -V 告诉你处在什么位置);用旧写法 PubkeyAcceptedKeyTypes,新版本仍把它当别名接受;或者给那一行加保护,让只有有能力的客户端才读到它:

Match all
    # only OpenSSH >= 8.5 gets here via Include ordering

——实践中,最简单的保护,是把对版本敏感的选项放进一个单独的文件,用 Include 拉进来,这样同一套 dotfiles 上的旧客户端就直接跳过它。同一种形状的报错(Bad configuration option: <something>)永远意味着同一件事:那个词对这个二进制文件来说是未知的——先查该选项的拼写,再查这个二进制文件的版本。

12. did not receive identification string / invalid ssh identification string

两者都住在 SSH 最开头的那一步——横幅交换,在任何加密之前。did not receive identification string from <ip> 是 sshd 在服务器端记下的,当有东西向 22 端口开了一个 TCP 连接、却从没发过 SSH-2.0-... 那一行时:一个端口扫描器、一个负载均衡器健康检查、一个监控探针,或者一个放弃了的客户端。在你的服务器日志里,它几乎总是背景噪声,而非故障——除非那是你自己的客户端连不上,那样的话,路径上有什么东西(一个代理、一个 TCP wrapper、一个 IDS)正在吃掉横幅。

kex_exchange_identification: ... invalid ssh identification string 是镜像的另一面:有东西确实应答了,但发来的是非 SSH 的数据。常见原因,按顺序:你把 ssh 指向了错误的端口——一个 HTTP 服务器、一个数据库、一个返回 HTML 的强制门户;路径上的一个中间盒在拦截连接、替服务器作答;或者一个 ProxyCommand/ProxyJump 配错了,把一个不是 SSH 流的东西管进了客户端——一个代理的错误页出现在了本该是横幅的地方。确认你确实在和一个 sshd 对话:nc <host> 22 应当立刻打印一个 SSH-2.0-OpenSSH... 横幅。如果它打印的是 HTML 或者什么都没有,那是端口(或主机,或代理跳转)错了,不是你的密钥。

13. Unspecified GSS failure / debug1: unspecified GSS failure. Minor code may provide more information

Kerberos 噪声。OpenSSH 默认会在你的密钥之前尝试 GSSAPI(Kerberos)认证,而如果你不在一个 Kerberos 域里——大多数人都不在——它会失败并在 debug 级别记下这条,然后回退到密钥认证、正常连上。**如果连接最终成功了,这一行是无害的;忽略它。**它只在你确实依赖 Kerberos SSO、而认证正在失败时才要紧,那种情况下它是一个真正的票据/域问题(检查 klist、时钟偏移,以及 DNS 里该主机的 SPN)。要在非 Kerberos 的配置上消掉这噪声,往你的配置里加上 GSSAPIAuthentication no。


让修复固化下来

这些里有一半是一次性的修复;另一半值得在 ~/.ssh/config 里写一行,好让它们再也不回来——保活、IdentitiesOnly、按主机区分的端口和身份。配置胜过记忆。

另一样胜过记忆的,是一款替你保存状态的客户端。Termalin 把每台主机的设置——端口、用户、密钥、保活——和主机本身一起存着,在首次连接时核实服务器,并在指纹变化时大声警告,而它的状态栏实时显示机器的生命体征,于是“这台机器到底在不在?”不再是一个诊断步骤。Free 版主机数量不限——拿去用,把这些报错里的几个永久退役。

先在一台主机上试试。

Termalin 是为你和你的智能体打造的高速 SSH 客户端。

免费版 · Pro 14 天试用 · 价格