SSH 连接被拒绝 vs 连接超时:故障排查指南
SSH 报错 Connection refused 与 Connection timed out 有本质区别。前者意味着服务器已响应但端口未监听,后者则代表数据包在途中丢失或被防火墙静默丢弃。本文教你通过错误响应时间与 TCP 状态快速定位问题根源,并提供针对性的修复方案。
SSH 中 “Connection refused” 和 “Connection timed out” 的含义
SSH 连接被拒绝(Connection refused)和连接超时(Connection timed out)是两种截然不同的故障,因此两者的解决方法完全不同。被拒绝意味着数据包已到达服务器,但服务器内核回复称“此处无进程在监听”。超时意味着数据包未到达任何可响应的对象,客户端在等待后放弃。被拒绝是服务器端的服务问题,而超时则是服务器前端的路径问题。
请仔细阅读客户端输出的具体错误行,因为措辞即是完整的诊断结果。
ssh: connect to host 203.0.113.10 port 22: Connection refused
ssh: connect to host 203.0.113.10 port 22: Connection timed out响应时间是第二个线索。被拒绝错误会立即返回,耗时仅相当于一次往返时间。超时错误则会挂起数秒后才显示,因为客户端在放弃前会持续重传。macOS 在相同情况下会输出 Operation timed out。如果您对该协议尚不熟悉,SSH 的工作原理及 sshd 的作用 是本指南所假设的背景知识。
为什么“Connection refused”是个好消息
Refused 是 TCP(传输控制协议)重置。客户端向 22 端口发送 SYN 数据包。数据包穿过互联网到达服务器的网络栈,内核发现该端口没有监听的 socket,因此回复一个 RST(重置)数据包。SSH 客户端将该 RST 转换为 Connection refused。
这个返回的数据包证明了很多信息。地址是正确的。主机已开机并能进行路由。路径上没有任何设备在静默丢弃发往该端口的流量,因为远端确实有响应。因此,所有剩余的嫌疑点都存在于服务器本身。
sshd未运行,因为它启动失败或从未被启用。sshd正在监听其他端口,通常是在进行加固配置后发生的。sshd绑定到了特定地址(如ListenAddress 127.0.0.1),因此只有服务器自身可以访问。- 防火墙被设置为拒绝(reject)而非丢弃(drop),因此防火墙代表主机发送了 RST。ufw 的
reject操作和以reject with tcp reset结尾的 nftables 规则都会执行此操作。
还有一种情况看起来类似但并非如此:你输入的地址属于另一台在线主机。该主机响应了你的 SYN,但其 22 端口没有 SSH 服务,因此礼貌地拒绝了你。在花费一小时排查错误的服务器之前,请先确认地址。了解 Linux 上监听端口的本质 有助于更快地阅读本节后续内容。
如何修复 Connection refused
你无法通过 SSH 修复此问题,因为 SSH 本身已中断。请打开服务商提供的 Web 控制台或串口控制台,登录后依次执行以下命令。
systemctl status ssh
sudo ss -tlnp
sudo sshd -T | grep -Ei '^(port|listenaddress|addressfamily)'systemctl status ssh 是 Ubuntu 和 Debian 上的单元名称。在 RHEL 及其衍生版(如 AlmaLinux)上,单元名称为 sshd。ss -tlnp 会列出所有处于监听状态的 TCP 套接字及其所属进程,这是判断问题的依据:如果输出中没有 sshd,则说明没有任何程序在监听,无论配置文件如何设置。sshd -T 会打印合并所有 Include 文件后的最终生效配置,若 /etc/ssh/sshd_config.d/ 中遗漏了端口,此处会显示出来。
请仔细阅读地址列。0.0.0.0:22 表示服务器上的所有 IPv4 地址。[::]:22 表示所有 IPv6 地址。127.0.0.1:22 表示仅限回环地址,因此所有远程连接都会被拒绝,而本地的 ssh localhost 连接则完全正常。
如果没有任何程序在监听,请启动该服务,并在启动失败时查看错误信息。
sudo sshd -t
sudo systemctl enable --now ssh
sudo journalctl -u ssh -n 50 --no-pagersshd -t 会解析配置文件并打印出错误指令所在的文件名及行号,且不会影响正在运行的服务。每次重启前请务必运行此命令,因为配置错误会导致 sshd 在启动时退出,从而导致下一次连接被拒绝。
Ubuntu 上的 socket activation 陷阱
Ubuntu 24.04 默认提供了一个用于 OpenSSH 的 systemd socket 单元。当该单元启用时,systemd 会接管监听端口,并为每个连接启动 sshd。因此,修改 Port 2222 中的 sshd_config 不会生效,服务器仍会在旧端口上响应。在进行任何编辑前,请先检查系统处于哪种模式。
systemctl is-enabled ssh.socket
systemctl status ssh.socket如果 socket 已启用,请在 socket 单元中设置端口,而不是在 sshd_config 中设置。
sudo systemctl edit ssh.socket[Socket]
ListenStream=
ListenStream=2222必须包含空的 ListenStream= 行,因为 systemd 的列表设置会追加到现有配置中。如果省略该行,服务器将同时在两个端口上监听。使用 sudo systemctl daemon-reload 和 sudo systemctl restart ssh.socket 应用更改,然后通过 sudo ss -tlnp 确认新端口已被成功接管。修改端口是 加固 VPS 上的 SSH 的常规步骤,这也是最常导致用户被锁在服务器外的操作。
为什么“Connection timed out”意味着没有任何响应
超时意味着静默。客户端发送了 SYN 包,在一两分钟内重试了多次,但始终未收到任何回包。此时无法证明服务器的任何状态,因为根本没有收到来自服务器的任何信息。
静默正是 DROP 规则产生的结果,丢弃是刻意为之。拒绝连接会告知扫描者主机存在,因此 ufw 和所有云服务商的网络防火墙都会直接丢弃不需要的数据包且不返回任何信息。超时通常是因为防火墙在您希望开放的端口上执行了拦截。
- 地址错误:DNS 记录仍指向已重构的服务器,或者输入错误导致访问了无人使用的地址。
- 主机未运行:主机已关机,或正处于重启过程中。从外部观察,因欠费被服务商停机表现出的现象也是如此。
- 主机防火墙丢弃了 22 端口的流量:这通常是因为在添加任何允许规则之前就运行了
ufw enable。 - 实例前端的云服务商防火墙丢弃了流量:操作系统根本没有收到该数据包。
- 您所在的网络阻止了出站 22 端口:这在办公网络和酒店网络中很常见。
从连接的另一端运行测试
这是最浪费时间的错误。如果数据包无法到达目标主机,您就无法从该主机内部诊断丢包问题。如果您能登录并运行命令,说明您根本没有遇到这个问题。本节中的所有命令均在您的本地机器上运行。
getent hosts vps.example.com
ssh -G vps.example.com | grep -Ei '^(hostname|port|user)'
ssh -vvv -o ConnectTimeout=10 user@vps.example.com
nc -vz -w 5 203.0.113.10 22getent hosts 会显示您的机器实际使用的地址,这能在几秒钟内发现过期的 DNS 记录。ssh -G 会打印您的客户端在读取 ~/.ssh/config 后应用的设置,因此它能发现旧的 Host 代码块,该代码块可能会静默重写主机名、端口或用户名。ssh -vvv 会显示连接尝试的进度:如果最后一行显示连接到某个地址后长时间停顿,则说明连接超时;如果显示远程 OpenSSH 版本,则说明 TCP 连接已成功,您真正的问题在于身份验证。在 Windows 上,PowerShell 中的 Test-NetConnection 203.0.113.10 -Port 22 可替代 nc。
测试端口,而不是主机。失败的 ping 无法证明任何问题,因为许多服务提供商会在边缘网络过滤 ICMP(互联网控制消息协议)。成功的 ping 也无法证明任何问题,因为它无法反映端口 22 的状态。
然后,更改任何命令都无法为您更改的变量:您的网络。尝试使用手机热点重试。如果热点可以连接而办公网络不行,说明拦截发生在您这一侧的网络,或者您的办公网络地址已被服务器封禁。
服务器外部的云服务商防火墙
大多数 VPS 控制面板都提供网络防火墙(有时称为安全组或云防火墙)。它运行在实例的上游,并维护独立的规则列表。ufw status 在服务器内部无法感知这些规则,这就是为什么 “我已经放行了 22 端口” 成为常见误区的原因。在修改服务器本地规则之前,请务必先登录控制面板查看该列表。
有一条命令可以解决这个问题,但它需要通过控制台访问。在服务器上运行该命令,然后尝试从笔记本电脑连接。
sudo tcpdump -ni any tcp port 22如果客户端尝试连接时没有任何输出,说明数据包在到达操作系统之前已被丢弃,故障源于服务商防火墙或路由问题。如果 SYN 数据包到达但没有回复,说明丢弃发生在本地,问题出在 ufw 或 nftables 上。这个简单的测试能将超时故障的排查范围缩小一半,因此非常值得通过控制台进行操作。
ufw 规则顺序、IPv6 以及自我封禁
ufw 的规则顺序错误是导致用户被拒之门外最常见的原因。sudo ufw enable 会立即应用默认的拒绝入站策略,因此如果在未配置 SSH 规则的情况下启用它,当前的会话会因已建立连接的状态而存活,但所有新连接都会超时。请务必先允许连接,再启用防火墙。
sudo ufw allow OpenSSH
sudo ufw status verboseOpenSSH 应用程序配置仅涵盖 22 端口。如果您计划将 SSH 端口迁移至 2222,则需要使用 sudo ufw allow 2222/tcp,且必须在修改端口配置之前添加该规则。更广泛的规则集请参考 VPS 的 ufw 防火墙基础,而安全的操作顺序则是 新 VPS 前十分钟的操作指南 的一部分。
IPv6 导致的超时现象往往令人困惑。如果主机名包含 AAAA 记录,客户端会优先尝试 IPv6 连接;此时如果服务器缺失 IPv6 规则,连接就会挂起,而纯 IPv4 的尝试却能成功。请手动区分这两者。
ssh -4 user@vps.example.com
ssh -6 user@vps.example.com如果 -4 可以连接而 -6 无法连接,问题出在服务器的 IPv6 规则上,在 ufw 中为 IPv6 开放相同端口 介绍了具体的解决方法。
您也可能不小心封禁了自己。fail2ban 会监控认证日志,并针对多次认证失败的地址插入防火墙规则。因此,错误的密钥或后台不断重试的脚本可能会导致整个办公室的 IP 被封禁。丢弃数据包的封禁表现为连接超时,而拒绝连接的封禁则会返回 No route to host。请在控制台执行:
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 198.51.100.24将您的地址添加到 ignoreip 是 Ubuntu 24.04 上 fail2ban 的有效配置 的一部分。
既非拒绝也非超时的错误
No route to host 表示收到了 ICMP 不可达消息。这说明要么是您的机器没有到达该网络的路由,要么是路径上的某个节点执行了管理性拒绝,这正是 iptables 的 REJECT 规则所发送的响应。
Network is unreachable 表示您的机器自身发出的错误。它完全没有该地址族的路由,当主机名仅解析为 IPv6 地址而连接仅支持 IPv4 时,通常会出现此错误。
kex_exchange_identification: Connection closed by remote host 表示 TCP 已连接,但服务器在密钥交换完成前挂断了连接。端口是开放的且 sshd 进程正常运行,请检查服务器负载、MaxStartups,或检查连接过程中是否触发了封禁。
Permission denied (publickey) 表示您已到达身份验证阶段但验证失败。网络和防火墙均正常,因此本指南中的内容均不适用。请转至 修复 SSH 上的 Permission denied (publickey) 进行排查。
如何恢复访问及避免再次被锁定
所有正规的 VPS 服务商都会提供一种不依赖于客户机网络的控制台:串行控制台或基于浏览器的 VNC 屏幕。这是本指南两个分支的恢复路径,因为即使 sshd 停止运行或防火墙规则丢弃了所有流量,它依然有效。在控制面板中找到它,以 root 或普通用户身份登录,然后执行上述检查。如果你从未设置过 root 密码,大多数面板都可以为你重置。
如果没有控制台,最后的手段是服务商提供的救援模式。它会引导进入一个精简的恢复系统并挂载你的磁盘,这样你就可以离线编辑 /etc/ssh/sshd_config 或删除防火墙规则,然后重启。
养成两个习惯可以防止下次被锁定。在编辑 sshd 或防火墙时,始终保持第二个 SSH 会话处于打开状态,因为当你测试新会话时,旧会话会因已建立的状态而保持连接。此外,在进行高风险的防火墙变更前,为自己设置一个自动撤销机制。
sudo systemd-run --unit=ufw-rollback --on-active=10min /usr/sbin/ufw disable
sudo systemctl stop ufw-rollback.timer第一行命令计划在 10 分钟后关闭 ufw。应用新规则后,打开一个新的 SSH 会话验证其是否生效,然后运行第二行命令取消回滚。如果你不慎将自己锁定,只需等待 10 分钟,防火墙就会自动关闭。这会使服务器处于无过滤状态,直到你再次启用 ufw,因此请仅在操作期间使用此方法,不要将其作为永久配置。
排查工作的顺序
- 阅读错误文本,并注意其出现的时间间隔。
- 若显示 Refused:前往控制台并检查
sudo ss -tlnp,确认监听套接字、端口及其绑定的地址。 - 若显示 Timed out:从本地机器确认地址,检查服务商控制面板中的防火墙,再检查服务器本地防火墙。
- 若不属于上述两种情况:说明 TCP 连接已建立,请将其视为身份验证或服务器负载问题,而非网络问题。
FAQ
为什么 sshd 正在运行,SSH 却提示 “Connection refused”?
因为拒绝连接的信号来自套接字而非服务本身,即使 sshd 正在运行也可能拒绝连接。请打开服务商提供的控制台并运行 sudo ss -tlnp。如果套接字绑定在 127.0.0.1:22 上,它会拒绝所有远程客户端,因为它仅绑定在回环地址上。如果套接字位于其他端口,则使用 22 端口的连接仍会被拒绝。如果使用了 systemd 套接字激活,端口由 ssh.socket 而非 sshd_config 指定,因此也请检查 systemctl is-enabled ssh.socket。此外,ufw 的 reject 规则也会代表主机返回拒绝信号,因此在下结论前请先阅读 sudo ufw status verbose。
为什么 ufw 已经允许了 22 端口,SSH 却仍然超时?
因为超时意味着没有收到任何响应,且 ufw 并非链路中唯一的防火墙。大多数 VPS 面板在实例前端运行网络防火墙,操作系统无法感知该防火墙丢弃的数据包。请在控制台运行 sudo tcpdump -ni any tcp port 22,并在运行该命令的同时尝试从笔记本电脑连接。如果没有数据包到达,说明丢弃发生在面板的上游;如果有数据包到达但没有回复,说明丢弃发生在本地的 ufw 或 nftables 中。
ping 不通是否意味着我的 VPS 已宕机?
不是。许多服务商会在网络边缘过滤 ICMP,因此即使服务器正在正常处理流量,也可能忽略你发送的所有 ping 请求。反之,ping 通也无法说明问题,因为它无法证明 22 端口是否开放。请使用你本地机器上的 nc -vz -w 5 203.0.113.10 22,或 Windows PowerShell 中的 Test-NetConnection 203.0.113.10 -Port 22 直接测试端口。
我修改了 SSH 端口,现在无法连接,哪里出了问题?
这通常由两种顺序错误导致。如果防火墙未添加新端口的规则,对新端口的连接会超时,而 22 端口会拒绝连接,因此 sudo ufw allow 2222/tcp 应在修改端口前执行,而非修改后。如果服务器使用 systemd 套接字激活 SSH,则 sshd_config 中的 Port 2222 会被忽略,systemd 会继续占用旧端口,你可以通过 systemctl is-enabled ssh.socket 确认这一点。请通过服务商控制台进行恢复,修复对应的问题,待 sudo ss -tlnp 显示新套接字已就绪后,再使用 ssh -p 2222 user@203.0.113.10 进行连接。