看报错,先分类
SSH 客户端给的报错其实很明确,只是很多人一急就跳过,直接开始瞎试。先按下面这张表对号入座,能省掉大半时间。
| 报错 | 卡在哪一步 | 方向 |
|---|---|---|
| Connection timed out | 网络层,包发出去没回应 | 查网络、防火墙、服务器是否在线 |
| Connection refused | 到达了机器,但端口没服务 | 查 sshd 是否运行、端口号对不对 |
| Permission denied (publickey) | 认证阶段,密钥没被接受 | 查密钥、权限、用户名 |
| Host key verification failed | 主机指纹比对失败 | 查 known_hosts 记录 |
| Connection closed by remote host | 握手过程中被挂断 | 查服务端日志、连接数限制 |
分类看懂了,后面就是按类处理。别一上来就怀疑密码忘了,很多人的问题根本还没走到密码那一步。
超时:路不通
timed out 意味着你的包发出去之后没有任何回应。注意是没有任何回应,不是被拒绝。这个区别很重要,因为它排除掉了服务端主动拒绝的可能。
排查顺序从近到远。
先确认目标机是不是活着。ping 一下,如果连 ping 都不通,问题在网络层,跟 SSH 无关。注意有些云服务器默认禁 ping,ping 不通不代表机器挂了,这时候要看控制台。
再确认端口通不通。telnet 或者 nc 测一下目标端口,能看到连接建立就是通的。这一步能把网络问题和 SSH 问题分开。
# 测端口是否可达(超时说明被丢包或没开)
nc -vz -w 5 服务器IP 22
# 或者用 telnet
telnet 服务器IP 22
# 想看包走到哪一跳断了
traceroute 服务器IP # Linux / macOS
tracert 服务器IP # Windows
如果端口不通,常见原因就是防火墙。云服务器上有两层要查:安全组规则,以及机器内部的 firewalld 或 ufw。两层都放行了才算通。很多人只改了安全组,忘了机器内防火墙还拦着。
还有一种情况是 IP 被封了。反复输错密码或者被安全策略判定为暴力破解,对方可能会临时封禁你的来源 IP。换个网络试试,手机热点也行,能连通就说明是这个原因。
拒绝:端口没人在听
refused 反而是好消息。对方的机器明确回了一个拒绝包,说明网络是通的,包能到达那台机器。问题在于那个端口上没有程序在监听。
两个方向查。一是 sshd 服务是不是挂了,二是端口号是不是改过。
sshd 挂掉的情况不少见。磁盘写满、内存耗尽、配置语法错误导致重启失败,都会让服务消失。这时候得通过云控制台的 VNC 或者救援模式登进去看。
端口改过也很常见。很多服务器出于安全考虑把 22 改成了别的端口,但你还是按默认的连。问一下管理员,或者看看有没有文档记录。连接时用 -p 指定端口。
# 指定非默认端口
ssh -p 2222 user@服务器IP
# 服务端确认 sshd 在跑(需已登录或本地终端)
systemctl status sshd
ss -lntp | grep sshd
# 看配置里实际监听的端口和地址
grep -E '^Port|^ListenAddress' /etc/ssh/sshd_config
还有一种容易忽略的情况:sshd 配置了只监听某个网卡或某个 IP,比如只监听内网地址。这样外网连过来自然被拒。看配置里的 ListenAddress 就能确认。
认证失败:密钥没被接受
Permission denied (publickey) 是最常见也最容易自己搞错的一类。它表示连接建立成功了,服务端也正常,但你的密钥没通过验证。
按这个顺序查,从最常犯的错开始。
第一是权限。SSH 对密钥文件的权限要求很严格,私钥必须只有自己能读。如果权限是 644 或者 777,客户端会直接拒绝使用这个密钥,有些版本会给出提示,有些只是静默跳过,表现就是一直认证失败。
# 修权限(Linux / macOS)
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
# Windows 上要给私钥去掉继承权限,只留当前用户
icacls id_rsa /inheritance:r
icacls id_rsa /grant:r "%USERNAME%:R"
Windows 上这个问题特别常见,尤其是从别处拷来的密钥文件,默认继承了父目录的权限,导致 SSH 拒绝使用。
第二是用户名。每台机器的每个用户都有自己的 authorized_keys。用 root 连和用 deploy 连是两套完全独立的密钥配置。很多人把公钥加到了错误的用户下面,然后一直用另一个用户名连,自然不行。
第三是密钥到底有没有加到服务端。确认 pub 文件的内容确实追加到了目标用户的 ~/.ssh/authorized_keys 里,而且是一行,没有被换行拆开。复制粘贴时多一个换行就会失效,这种错误肉眼几乎看不出来。
第四,用 -v 让客户端把所有细节打出来。它会明确告诉你试了哪些密钥、服务端接受了哪一种认证方式、最后为什么失败。
# 详细模式,能看到提供了哪个密钥
ssh -v user@服务器IP
# 更详细,连密钥交换过程都打出来
ssh -vvv user@服务器IP
# 指定使用某个私钥
ssh -i ~/.ssh/my_key user@服务器IP
# 客户端里指定时,配置文件写法
# ~/.ssh/config
Host myserver
HostName 服务器IP
User deploy
Port 2222
IdentityFile ~/.ssh/my_key
如果客户端这边看不出问题,就去服务端看日志。Linux 上一般是 /var/log/auth.log(Debian 系)或者 /var/log/secure(RedHat 系),那里会写清楚拒绝的具体原因。
主机指纹变了
Host key verification failed 的意思是:你本地记录的服务器指纹,和这次连上的服务器指纹不一致。
绝大多数情况是正常的,比如服务器重装了系统、换了密钥、或者同一 IP 换了另一台机器。但也不能排除中间人攻击,所以处理方式要讲究一点。
正确的做法是先用别的渠道确认服务器指纹真的变了。云服务商的控制台一般能查看,或者让同事从他那边连一次对比指纹。确认无误之后,再去本地删掉对应的那一条记录。
# 看本地记录的指纹
ssh-keygen -F 服务器IP
# 删掉某个主机的记录
ssh-keygen -R 服务器IP
# 连的时候顺便打印对端指纹,用于人工核对
ssh -o VisualHostKey=yes user@服务器IP
反过来,如果你没做任何变更,指纹却莫名其妙变了,那就先别连。这时候删记录重连等于把安全提示关掉,风险自己承担。
服务端侧的问题
有时候客户端怎么查都正常,问题出在服务器自己身上。几个高频的。
磁盘满了会让 sshd 无法写入日志或者会话文件,表现是能连上但立刻断开,或者连接直接被关。用 df -h 看一眼根分区。
连接数打满也是常见的,maxsessions 或者 MaxStartups 到了上限,新连接会被拒。
还有 fail2ban 这类防爆破工具,短时间内多次尝试失败就会封禁来源 IP。如果你刚才反复试了很多次,很可能已经被自己封了,需要从控制台解封。
最后是配置改了没重启。sshd_config 修改后必须 reload 才生效,有人改了配置发现没变化,其实是没重启服务。
一个通用排查顺序
把上面这些压缩成一条可以照着走的流程。
第一步,读报错归类,确定卡在网络、端口还是认证。第二步,用 nc 或 telnet 测端口,把网络问题和 SSH 问题切开。第三步,如果能连上但认证失败,先用 ssh -v 看客户端行为,再去看服务端认证日志。第四步,如果连端口都不通,查安全组、机器防火墙、sshd 进程、监听端口。第五步,任何时候都别忘了确认服务器本身还活着,磁盘没满,服务没被防爆破工具封掉。
顺序别乱。见过太多人一上来就删 known_hosts、重装客户端、换密钥,折腾一圈发现是安全组没放行端口。
常见问题 FAQ
Connection timed out 和 Connection refused 有什么区别?
timed out 是一点回应都没有,请求像石沉大海。通常是网络不通、防火墙静默丢包,或者服务器根本没开机。refused 是对方明确回了一个拒绝包,说明网络是通的,能到达那台机器,只是那个端口上没有服务在监听,或者被防火墙主动拒绝。看到 refused 反而是好消息,至少证明路是通的。
Permission denied (publickey) 怎么排查?
按这个顺序查:一,密钥文件权限必须是 600,目录是 700,权限太宽 ssh 会拒绝使用;二,确认连的是不是对的用户,不同用户有各自的 authorized_keys;三,用 ssh -v 看客户端到底提供了哪个密钥;四,去服务器端看 /var/log/auth.log 或 secure 日志里记录的原因。最常见的是权限问题和用错用户。
为什么昨天还能连今天就不行了?
常见原因有几个:服务器更换了密钥或者重置了系统;你的 IP 变了导致安全组规则不再匹配;有人在防火墙里加了封禁;服务器的磁盘满了导致 sshd 无法写入必要文件;或者 sshd 配置被改过没重启。先看 sshd 进程还在不在,再看服务器本地能不能登录,能排除掉一半可能。
Host key verification failed 是不是被攻击了?
多数不是。最常见的原因是服务器重装系统或更换了密钥,导致主机指纹变了,本地记录对不上。安全起见不要直接删掉记录重连,先用其他可靠渠道确认服务器指纹真的变了,再删 known_hosts 里对应的那一条。如果指纹确实莫名其妙变了而且你没做任何操作,那才需要警惕中间人攻击。