技术指南

SSH 连不上:按协议层定位并安全恢复

从 DNS、TCP、主机指纹到用户认证逐层定位 SSH 失败,并用带外入口安全恢复访问。

SSH 故障排查的关键不是反复重试,而是确认连接最后成功走到了哪一层。名称解析、TCP 连接、SSH 主机身份和用户认证是四个不同关口;只有保留一次完整诊断输出,才能避免把网络问题误修成密码问题,或在主机指纹变化时跳过安全警告。

先固定客户端真正使用的目标

别只看输入的别名。OpenSSH 会合并命令行、`~/.ssh/config`、系统配置、`Host` 和 `Match` 规则。`ssh -G` 会打印计算后的客户端配置;先记录真实主机名、地址族、端口、用户、代理跳板和身份文件,再做网络测试。

把示例目标替换为实际别名;输出不包含私钥内容,但共享前仍应检查内部主机名
[email protected]

ssh -G "$TARGET" | grep -E \
  '^(hostname|user|port|addressfamily|proxyjump|proxycommand|identityfile|identitiesonly|connecttimeout) '
getent ahosts example.com

若 DNS 同时返回 IPv4 和 IPv6,可以分别用 `ssh -4` 与 `ssh -6` 验证。某一个地址族失败不等于服务器整体离线;也不要因为 `ping` 失败就下结论,ICMP 与 SSH 的 TCP 路径和访问策略可以不同。

用一次详细连接标记最后成功层

诊断文件可能包含内部地址、用户名、公钥指纹和代理信息;脱敏后再分享,并在工单结束后按策略删除
ssh -vvv \
  -o ConnectTimeout=8 \
  -o ConnectionAttempts=1 \
  -o BatchMode=yes \
  -p 22 [email protected] true 2>&1 | tee ssh-diagnostic.log
最后看到的现象已经证明下一步
Could not resolve hostname客户端未得到可用目标地址检查别名、DNS、搜索域和客户端配置
No route / timed out尚未完成 TCP 或 SSH 初始握手检查地址族、路由、两层防火墙和外部探测
Connection refused目标地址返回了拒绝核对端口、监听进程、socket activation 和映射
REMOTE HOST IDENTIFICATION HAS CHANGED收到了与本地记录不同的主机密钥停止认证并通过独立渠道核对指纹
Permission deniedTCP、SSH 握手和主机身份阶段已通过检查用户、所用密钥、服务端策略与日志
认证后立即断开凭据可能已被接受检查账号 shell、PAM、ForceCommand、资源和会话日志

认证之前先证明 TCP 与 SSH 握手

从实际故障网络执行;再从另一条受控网络重复一次,记录 UTC 时间和出口地址
nc -vz -w 5 example.com 22
ssh -vvv -o ConnectTimeout=8 -o BatchMode=yes \
  -p 22 [email protected] true

若所有外部网络都超时而控制台可用,依次核对实例公网地址、云侧入站规则、主机路由和主机防火墙。只有一个办公网失败时,优先调查该网络的出口 ACL、代理、运营商路径或 IPv6。`nc` 成功只说明 TCP 端口可连接,不证明对端身份,也不证明认证会成功。

从控制台核对监听者与实际配置

Ubuntu 可能同时存在 `ssh.socket` 和按需启动的 `ssh.service`;其他发行版常用 `sshd.service`。不要机械地重启一个猜测的单元。先看监听端口及拥有它的进程,再看 unit 关系、配置语法和包含 snippets、`Match` 条件后的有效值。

替换用户、主机和客户端地址;`-C` 用于评估依赖连接条件的 Match 规则
sudo ss -lntp | grep -E ':(22|2222)\b'
systemctl status ssh.service ssh.socket sshd.service --no-pager

sudo sshd -t
sudo sshd -T -C user=admin,host=example.com,addr=198.51.100.24 | \
  grep -E '^(port|listenaddress|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|authorizedkeysfile|strictmodes) '

主机密钥变化必须先核对指纹

OpenSSH 在已知主机密钥变化时阻止继续认证,用于防止服务器欺骗和中间人攻击。正常重装、IP 复用或主机密钥轮换也会触发同样警告,所以警告本身不能证明攻击,也不能直接忽略。通过云控制台或另一个受信管理通道读取服务器公钥指纹,并与客户端警告逐字符比较。

默认 22 端口可使用主机名;非默认端口使用 [host]:port,避免误删其他记录
# 在可信控制台上读取当前服务器指纹
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

# 仅在指纹和变更原因都确认后,在客户端删除精确目标
ssh-keygen -R '[example.com]:2222'
ssh -o StrictHostKeyChecking=ask -p 2222 [email protected]

`ssh-keyscan` 可以收集对端当前提供的公钥,但它不会认证这些公钥。不要把同一条可能已被劫持的网络路径同时当作告警来源和可信指纹来源。

Permission denied 要对齐客户端与服务端

`Permission denied` 表示连接已经进入认证阶段。先用 `IdentitiesOnly=yes` 限制客户端只提交指定密钥,避免代理中大量密钥导致 `MaxAuthTries` 提前耗尽;同时在控制台跟随实际 SSH unit 的日志,确认服务器看到的用户、密钥指纹和拒绝原因。

详细日志会显示提交的公钥指纹;不要在聊天或工单中粘贴私钥
ssh -vvv \
  -o IdentitiesOnly=yes \
  -i ~/.ssh/id_ed25519 \
  -p 22 [email protected]

# Debian / Ubuntu 常见;按实际 unit 名调整
sudo journalctl -fu ssh.service
  • 确认目标用户名存在、未锁定、登录 shell 有效,并且 `AllowUsers` / `AllowGroups` / `DenyUsers` / `Match` 没有排除它。
  • 用 `ssh-keygen -lf ~/.ssh/id_ed25519.pub` 记录客户端公钥指纹,与服务端 `authorized_keys` 中的目标行核对。
  • 读取 `sshd -T -C ...` 的 `PubkeyAuthentication`、`AuthorizedKeysFile`、`PasswordAuthentication` 和 `PermitRootLogin`,不要只搜索主配置文件。
  • 检查 home、`.ssh` 和 `authorized_keys` 的所有者及可写权限;`StrictModes` 默认会检查用户文件和 home 的 ownership/mode。
  • 若认证成功后断开,继续检查 shell、PAM、磁盘/进程限制、`ForceCommand` 和会话日志,而不是重复换密钥。
先替换真实用户并检查现状;不要对整个 home 目录递归 chown
namei -l /home/admin/.ssh/authorized_keys
sudo -u admin ssh-keygen -lf /home/admin/.ssh/authorized_keys

sudo chmod go-w /home/admin
sudo chmod 700 /home/admin/.ssh
sudo chmod 600 /home/admin/.ssh/authorized_keys
sudo chown admin:admin /home/admin/.ssh /home/admin/.ssh/authorized_keys

修改端口或策略时设置验收门

Ubuntu 官方文档建议把自定义放进 `/etc/ssh/sshd_config.d/*.conf`,并在重启前执行 `sshd -t`。但包含顺序、first-set 语义、socket activation 和服务名会随发行版改变;必须以当前主机文档、`systemctl cat` 和有效配置输出为准。

  • 确认带外控制台可用,保留当前已认证会话并记录回滚命令。
  • 若改端口,先在云防火墙与主机防火墙增加只对管理来源开放的新端口,不先删除旧规则。
  • 写入单一、可审计的配置变更,运行 `sshd -t` 并用 `sshd -T -C ...` 验证目标用户。
  • 按实际 service/socket 模型重载或重启,从第二个终端验证新端口、主机指纹、密钥登录和 sudo。
  • 查看日志和监听状态;验收全部通过后,才移除旧端口和旧规则。

完全失联时按可逆性恢复

  1. 从服务商控制台确认实例运行状态、当前公网地址和启动日志。
  2. 通过串行控制台、VNC 或 Web 控制台检查监听、journal、磁盘空间和最近配置变更。
  3. 进入救援系统后以只读方式检查系统盘,再挂载并修复配置、密钥 ownership 或防火墙;保留修改记录。
  4. 若最近变更有已验证快照,先评估数据回退范围再回滚。
  5. 重装只作为最后手段;先确认数据盘、独立备份、主机密钥变化和 DNS/IP 后续影响。

用隔离高端口演练错误边界

VPScope 在回环地址的 22222 端口启动独立 OpenSSH 9.6p1 daemon,使用临时 host/client keys 和禁用密码的专用配置。未启动时客户端得到 `Connection refused`;启动后错误密钥完成 TCP 与主机密钥握手,再得到 `Permission denied (publickey)`;正确密钥执行了合成命令。

随后轮换临时 host key,已记录旧指纹的客户端以状态 255 阻止连接。只有模拟从可信控制台读取新 SHA256 指纹、删除精确 `[host]:port` 记录并重新确认后,连接才恢复。测试未修改端口 22、系统 SSH unit、生产配置或防火墙,结束后 daemon、端口和全部临时文件均已清理。

常见修复动作的风险

动作为什么危险更小的修复
删除整个 known_hosts丢失所有已建立的主机身份记录核对指纹后只删除精确 host:port
临时开放 SSH 到全网扩大暴力尝试和凭据攻击面只对受控管理来源开放并设置回收时间
递归 chown 整个 home可能破坏应用、部署和共享文件所有权只修复 home、.ssh 与目标 key file
直接重启 sshd 试错语法或监听错误可能切断唯一入口先 sshd -t、有效配置检查和第二会话验收
启用 root 密码登录把恢复问题变成高权限口令暴露恢复指定普通管理用户的密钥路径

关闭事件前留下可复查记录

  • 记录 UTC 时间、客户端网络、目标解析结果、有效 user/host/port 和最后成功协议层。
  • 保存脱敏后的 `ssh -vvv` 关键行、监听进程、有效 sshd 配置和对应 journal 时间窗。
  • 主机密钥变化有独立渠道指纹和变更原因;没有通过跳过检查恢复。
  • 认证恢复后验证新会话、sudo、第二管理入口和业务健康,而不是只看端口开放。
  • 撤销临时防火墙规则、测试密钥与宽松策略,并记录永久修复和下一次演练日期。
  • 检查发行版 OpenSSH 安全更新;本次测试安装版本低于当前 Ubuntu 仓库候选补丁。

返回知识库