SSH 连接故障排查是每一位运维和开发者都绕不开的功课。无论是本地连不上云服务器,还是跳板机突然拒绝登录,报错信息往往只有寥寥几个单词:Connection timed out、Connection refused、Permission denied (publickey),或者那句让人一愣的 REMOTE HOST IDENTIFICATION HAS CHANGED。本文用一套“四步定位法”,带你从网络层一路查到认证层,10 分钟内定位绝大多数 SSH 登录失败的根因。
一、先分类:你遇到的是哪种“连不上”
SSH 连不上,本质是 TCP 连接或应用层认证在某个环节断了。把现象分好类,排查就成功了一半。常见报错对应的大致层级如下:
| 报错关键词 | 断裂层级 | 最可能原因 |
| Connection timed out | 网络层 | 防火墙/安全组拦截、路由不通、IP 错误 |
| Connection refused | 端口与服务层 | sshd 未启动、监听端口不对、监听地址受限 |
| Permission denied (publickey) | 认证层 | 密钥权限错误、authorized_keys 未生效、配置禁用密码 |
| REMOTE HOST IDENTIFICATION HAS CHANGED | 主机指纹层 | 服务器重装/换 IP、known_hosts 缓存冲突 |
| Write failed: Broken pipe | 会话保持层 | 空闲超时、NAT 回收、KeepAlive 未开启 |
二、四步定位法:从网络到认证逐层排查
不要把所有命令一股脑试一遍。按“网络 → 端口 → 服务 → 认证”的顺序,每一层用一个最小命令验证,能定位到哪一层断,就解决了 80% 的问题。第一步永远是先开详细日志:
ssh -v user@203.0.113.10
# 还看不清,就开最大级别
ssh -vvv user@203.0.113.10
-v 会打印出“正在尝试哪个 IP、连哪个端口、用的是哪把密钥、卡在哪一步”,是 SSH 连接故障排查里最有价值的单条命令。日志里出现 Connecting to ... port 22 之前就失败,是网络层;连上端口后被服务器主动断开,多半是服务或认证层。
三、Connection timed out:网络与防火墙层
超时意味着 SYN 包有去无回。先用 ping 确认三层可达,再用 telnet 或 nc 单独测 22 端口,避免被“能 ping 通”误导(很多云主机禁 ICMP):
# 测端口是否通(比 ping 更准)
nc -vz 203.0.113.10 22
# 或
telnet 203.0.113.10 22
# 端口扫描看 22 是否开放
nmap -p 22 203.0.113.10
如果端口测不通,按这个清单逐项核对:云厂商安全组是否放行进站 22;服务器本地防火墙(iptables/ufw/firewalld)是否放行;目标 IP 是否正确(公网 IP vs 内网 IP);以及本地网络是否屏蔽了 22 出口。在容器或内网环境里,常常还需要确认 Docker 网络模式 与端口映射是否正确。
四、Connection refused:端口与服务层
4.1 确认 sshd 是否在跑
端口通但被拒绝,通常是目标机上的 sshd 没监听。登录控制台(VNC/云终端)后检查:
systemctl status sshd
# 没跑就拉起
sudo systemctl start sshd
sudo systemctl enable sshd
# 看 22 端口到底有没有在监听,以及监听的是哪个地址
ss -tlnp | grep ':22'
# 或老命令
netstat -tlnp | grep ':22'
如果 ss 显示监听的是 127.0.0.1:22 而不是 0.0.0.0:22,说明 sshd 只接受本机连接,外部自然 refused。在 /etc/ssh/sshd_config 里把 ListenAddress 改掉或注释掉,再 systemctl restart sshd 即可。
4.2 端口被改过
很多主机为了防扫描会把 SSH 换到高位端口(如 2222)。此时必须显式指定端口,否则默认 22 必然是 refused:
ssh -p 2222 user@203.0.113.10
# 或写进 ~/.ssh/config 一劳永逸(见第八节)
五、Permission denied (publickey):密钥与认证层
能连上端口却登不进去,且服务端禁用了密码登录,就会卡在 publickey。这是 SSH 连接故障排查里最常被忽略的一环——不是密钥不对,而是权限不对。Linux 对 ~/.ssh 及其下文件的权限极其严格,任何“过松”的权限都会让 sshd 直接拒绝:
# 修复客户端密钥与目录权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
# 修复服务端 authorized_keys 与 home 目录权限
chmod 700 ~/.home/$USER
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
然后用 ssh-copy-id 把公钥正确送上去(它会自动处理权限),比手动 cat pub >> authorized_keys 更稳妥:
ssh-copy-id -p 22 user@203.0.113.10
服务端也要确认 /etc/ssh/sshd_config 里 PubkeyAuthentication yes、PasswordAuthentication no(若你要密钥登录)、以及 AuthorizedKeysFile .ssh/authorized_keys 路径正确。改完配置务必 sudo sshd -t 做语法检查,再重启,避免把自己锁在门外。
六、REMOTE HOST IDENTIFICATION HAS CHANGED:known_hosts 冲突
这句警告是 SSH 的安全机制在保护你:服务器的主机指纹和你本地 ~/.ssh/known_hosts 里记的不一致。常见于服务器重装系统、更换弹性 IP、或内网 IP 被复用给另一台机器。若你确认是合法变更(例如自己刚重装),清掉旧记录即可:
# 删除指定主机的旧指纹
ssh-keygen -R 203.0.113.10
# 带端口的情况
ssh-keygen -R "[203.0.113.10]:2222
# 然后再连,按提示输入 yes 重新写入
ssh user@203.0.113.10
⚠️ 在清记录之前,请务必通过云控制台等带外通道确认服务器确实是你自己的。中间人攻击也会触发同样的提示,盲目 yes 可能把密钥交给攻击者。
七、Broken pipe / 连接被重置:空闲断开与 KeepAlive
能登录、能操作,但放着不动几分钟就掉线,报错 Write failed: Broken pipe 或 Connection reset by peer。这通常是 NAT/防火墙回收了空闲连接,或服务器主动踢掉空闲会话。靠 KeepAlive 心跳解决:
# 客户端 ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
# 服务端 /etc/ssh/sshd_config(让服务器主动探活)
ClientAliveInterval 60
ClientAliveCountMax 3
ServerAliveInterval 60 表示每 60 秒发一次心跳,连续 3 次无响应才断开,足以撑过大多数 NAT 超时窗口。改完服务端配置记得 sudo systemctl restart sshd。
八、进阶:跳板机、ProxyJump 与多账号 config
生产环境常常需要经跳板机访问内网机器。与其手动两次 ssh,不如用 ProxyJump 一条命令穿透,也顺手解决了“内网 IP 在外网连不上”的问题:
# 命令行直连
ssh -J jump@203.0.113.10 user@10.0.0.20
# 写进 ~/.ssh/config,以后 ssh internal 即可
Host jump
HostName 203.0.113.10
User jump
Port 22
Host internal
HostName 10.0.0.20
User deploy
ProxyJump jump
IdentityFile ~/.ssh/id_ed25519_internal
多账号、多密钥的场景也建议全部收敛到 ~/.ssh/config,用 Host 分段管理,避免每次手敲一长串参数。在 VS Code 远程开发 里,这份 config 还能被 Remote-SSH 插件直接复用,体验非常顺滑。
九、SSH 连接故障速查表
| 现象 | 第一步该跑的命令 | 典型修复 |
| timed out | nc -vz host 22 | 放安全组/防火墙、核对公网 IP |
| refused | ss -tlnp | grep :22 | 启动 sshd、修正 ListenAddress/端口 |
| Permission denied | ssh -v 看用哪把 key | 修 600/700 权限、ssh-copy-id |
| host key changed | ssh-keygen -R host | 确认合法后清 known_hosts |
| Broken pipe | 查空闲时长 | 开 ServerAliveInterval/ClientAlive |
十、防御式配置:把“连不上”挡在发生之前
排查是救火,预防才是正道。三条低成本建议能显著降低 SSH 连接故障的发生概率:其一,禁用密码登录、仅用密钥(PasswordAuthentication no),顺带挡掉暴力破解;其二,改到高位端口 + Fail2ban,让扫描器少来烦你;其三,保留一个带外通道(云控制台/VNC),一旦 sshd 配置失误被锁门外,还能救回来。把 ~/.ssh/config 纳入 GitHub Actions 或配置管理,团队换人也不怕丢失登录方式。
SSH 连接故障排查并不神秘,核心就是“分层定位、逐层验证”。把本文的四步法(网络→端口→服务→认证)和速查表存好,下次再遇到连不上,按图索骥,基本都能在十分钟之内恢复远程登录。




