Connection refused 和 Connection timed out 看起来像一对兄弟——反正 SSH 都没连上——但它们是相反的结论,指向技术栈的不同半边。搞清楚你看的是哪一个,能在你敲第一条诊断命令之前,就把嫌疑名单砍掉一半。这是一套两分钟的流程:每个报错各证明了什么、把它们与其他一切区分开的那一个探测,以及每个分支各自的清单。(对于 TCP 连接成功之后才发生的报错——认证失败、主机密钥警告、会话死掉——见那枚解码环。)
| 报错 | 它证明了什么 | 可能的原因 | 第一条命令 |
|---|---|---|---|
Connection refused | 主机在线,并主动拒绝(回了一个 RST) | 那个端口上没东西监听,或者有一条 REJECT 防火墙规则 | 从控制台执行 ss -tlnp | grep sshd |
Connection timed out | 什么都没回来——路径上一片沉默 | IP 错了、主机宕了,或者一个静默的 DROP(云安全组 / 防火墙) | nc -vz host 22,然后检查云防火墙 |
每个报错到底证明了什么
**Connection refused 意味着机器应答了你。**你的 TCP SYN 到达了一台活着的主机,而主机主动说了不——回了一个 RST 包或一个 ICMP 端口不可达。那其实是相当多的好消息:IP 是对的,路由是通的,主机在线,而且路径上没有东西在吞你的包。问题很窄:那个端口上没有东西在监听,或者有一条防火墙规则在拒绝(而不是丢弃)它。
**Connection timed out 意味着沉默。**你的包出去了,而什么都没回来过。那几乎证明不了关于服务器的任何事——它可能宕了,IP 可能错了,或者(最常见)你和它之间有东西在静默地丢弃包:一个云安全组、一个带 DROP 策略的防火墙、一个 NAT 或 VPN 黑洞。
一个助记:**refused = 机器对你说了话;timed out = 它从没开口。**refused 是主机问题。timed out 通常是路径问题。
第 1 步:探测端口,不是主机
别把 ping 当你的第一招——它在两个方向上测的都是错的东西。一台应答 ping 的主机仍然可能丢弃 22 端口(云防火墙按端口过滤),而一台无视 ping 的主机可能正愉快地提供 SSH(ICMP 到处都被封)。ping 证明不了任何你需要的东西。
探测真正的端口:
nc -vz example.com 22
(没有 netcat?telnet example.com 22 也行——任何以 SSH-2.0-... 开头的输出,都意味着 sshd 应答了。)三种结果:
Connection refused——走下面的 refused 分支。- 挂住,然后超时——走 timed-out 分支。
- 成功——TCP 没问题,你的问题在 SSH 自身内部:用错用户或密钥(
Permission denied (publickey)有自己的指南(英文))、主机密钥不匹配,或者某个ssh -v会直接点名的东西。
refused 分支:找出什么(没)在监听
你会需要另一条上机的路——服务商的网页控制台正是为这种时刻而存在的。
sshd 在运行吗?
systemctl status sshd # the unit is "ssh" on Debian/Ubuntu
如果它死了,别只是重启——查为什么:journalctl -u sshd(或 -u ssh)会显示它是怎么退出的。一个常见原因是一处无法解析的配置改动;sshd -t 会校验文件、并在你重启进同一个失败之前点出出错的那一行。
它在监听你以为的地方吗?
sudo ss -tlnp | grep sshd
这段输出里藏着两个坑。端口:有人把 SSH 挪到了 2222,而你在敲 22——用 ssh -p 2222 连,然后在 ~/.ssh/config 里(英文)按主机把它钉死,这样你就再也不用重新推导它。地址:一条 ListenAddress 可以把 sshd 绑到单个网卡上,于是它在私有 IP 上应答、在公网 IP 上拒绝。
本地防火墙在拒绝吗?sudo ufw status、sudo iptables -L -n,或 nft list ruleset——找覆盖你端口的 REJECT 规则。一条 REJECT 规则产生的正是这个报错:主机在线,sshd 甚至可能在监听,而防火墙替它作答。
**你是不是被封了?**fail2ban 的默认动作是拒绝,所以一次封禁看起来就像 Connection refused——只从你的 IP。破绽:从手机热点能用,从你的网络不行。从控制台:fail2ban-client status sshd,然后 fail2ban-client unban <your-ip>——再修好那个“重试一个坏凭据、次数多到挣得一次封禁”的东西。
timed-out 分支:找出包死在哪里
**先确认地址。**这种事只会让人难堪恰好一次。ssh -v 会打印它实际拨的 IP——拿它和服务商的面板核对。迁移之后过期的 DNS,以及没能挺过一次实例停止的弹性 IP,都是经典。
**接着查云防火墙,因为它大多数时候就是答案。**安全组(AWS)、NSG(Azure)和 VPC 防火墙规则(GCP)默认丢弃入站流量——静默地,而那恰恰就是一次超时。找一条允许来自你地址的 TCP 22 的入站规则。阴险的变体:一条几个月前锁到“我的 IP”的规则,而你的 IP 自那以后变了。子网层面的网络 ACL 是第二层、较少被查看的一层,当安全组看起来没问题时值得一看。
追踪路径,当云规则都查过没问题时:
traceroute example.com # mtr example.com for a live view
sudo traceroute -T -p 22 example.com # TCP to the actual port — closest to the truth
普通的 traceroute 用 UDP 或 ICMP,防火墙对它们的处理和 TCP 22 不同——-T -p 22 这种形式测的是 SSH 实际经历的。跳数在哪里不再应答,大致就是你的包死在哪里。
**怀疑你自己这一侧。**酒店、咖啡馆和办公室的访客网络例行封禁出站 22,而企业 VPN 会重路由或丢弃它。隔离测试就是手机热点:如果 SSH 在那里能用,你的服务器没问题,你刚才所在的那个网络才是问题。
主机自身上的一个 DROP 策略防火墙,从外面看和云侧的丢弃一模一样——如果你有控制台访问,也去机器上查查 iptables/nftables。
**稀奇的那个:端口敲门(port knocking)。**一台跑着 knockd 的服务器,在看到其他端口上一串秘密的连接尝试之前,会丢弃一切。如果是前一个人配置了这台机器、而每个端口从每个地方都超时,先四处问问,别在路由理论上烧掉一个下午。
记住这个两分钟版本
整套流程压缩得很好:**用 nc -vz 探测端口。refused → 控制台登进去,检查 sshd、监听端口、REJECT 规则、fail2ban。timed out → 确认 IP,检查云安全组,然后 traceroute 和你自己的网络。**ping 在里面哪儿都不出现。
那些反复出现的事实——哪个端口、哪个用户、哪台主机需要保活——该放在 ~/.ssh/config 里(英文),而不是你的记忆里。而一部分诊断可以是常驻的状态,而不是事发时的手忙脚乱:Termalin 把端口、用户、密钥和保活设置和每台保存的主机一起存着,并按主机显示实时状态——于是“这台机器到底在不在?”和“它到底是哪个端口来着?”在你连接之前就有了答案,而那台只监听 2222 的服务器,不再是你每个季度都要重新发现一次的东西。
Termalin 是一款免费、跨平台的 SSH 客户端,把按主机的端口、用户、密钥和保活和主机一起存着,并显示实时的主机状态——下载它,或浏览完整功能列表。