SSH 连接故障排查指南

编写日期:2026-06-20
场景:从 Windows 环境连接远程服务器 [SERVER IP]:22,用户名 root,使用 Ed25519 私钥认证


1. 环境概述

组件 说明
客户端 OS Windows 11 + PowerShell 7
SSH 客户端 Windows 内置 OpenSSH(9.5p1)
服务器 OS Ubuntu 22.04.5 LTS
SSH 服务 OpenSSH_8.9p1
SSH 端口 22
密钥格式 OpenSSH 私钥(Ed25519)

2. 完整连接步骤(最终成功方案)

2.1 确认 TCP 连通性



powershell

Test-NetConnection [SERVER IP] -Port 22

⚠️ 经验:TcpTestSucceeded : False ≠ SSH 一定连不上。实际调试中发现 Test-NetConnection 超时时 SSH 客户端依然可以建连(见 3.3 节)。建议用 ssh -v -o ConnectTimeout=10 ... 代替端口探测作为更准确的联通性判断。

2.2 修复私钥文件权限(最关键步骤)

Windows OpenSSH 要求私钥文件 仅 当前用户和 SYSTEM 可读。以下命令逐一收紧:



powershell

# Step 1: 查看当前权限
icacls "D:\path\to\your\private_key"

# Step 2: 注销继承(移除所有继承权限)
icacls "D:\path\to\your\private_key" /inheritance:r

# Step 3: 移除多余权限条目(不要用 /deny!见下方警告)
icacls "D:\path\to\your\private_key" /remove "BUILTIN\Users" "NT AUTHORITY\Authenticated Users"

# Step 4: 重新授予必要权限
icacls "D:\path\to\your\private_key" /grant "BUILTIN\Administrators:(F)"
icacls "D:\path\to\your\private_key" /grant "NT AUTHORITY\SYSTEM:(F)"
icacls "D:\path\to\your\private_key" /grant "$env:USERNAME:(R)"

# Step 5: 验证结果
icacls "D:\path\to\your\private_key"
# 应仅显示 Administrators、SYSTEM 和当前用户

⚠️ 大坑警告:不要用 /deny,用 /remove

>

首次尝试时笔者误用了 /deny:
“powershell
icacls key /deny "BUILTIN\Users:(R)" /deny "NT AUTHORITY\Authenticated Users:(M)"
`
结果产生了
DENY ACE,DENY 优先级高于 ALLOW。当前用户属于 Authenticated Users,虽然显式的 [USER]:(R) 也存在,但 DENY 生效导致 SSH 仍然报告 Load key: Permission denied`。

>

正确做法是 /remove 直接删除条目,而不是加 DENY。

2.3 建立 SSH 连接



powershell

ssh -o StrictHostKeyChecking=no `
    -o UserKnownHostsFile=/dev/null `
    -o PreferredAuthentications=publickey `
    -o GSSAPIAuthentication=no `
    -o PasswordAuthentication=no `
    -o ConnectTimeout=10 `
    -p 22 `
    -i "D:\path\to\your\private_key" `
    user@[SERVER IP]

2.4 验证连接



powershell

ssh -o StrictHostKeyChecking=no `
    -o UserKnownHostsFile=/dev/null `
    -o ConnectTimeout=10 `
    -o PreferredAuthentications=publickey `
    -o GSSAPIAuthentication=no `
    -p 22 `
    -i "D:\path\to\your\private_key" `
    user@[SERVER IP] "hostname && uname -a"

3. 失败原因逐层分析

当天在 Codex CLI 环境中首次连接历时较长,涉及三个层次的问题:

3.1 第一层:执行策略阻断(Codex 环境特有)

Codex CLI 对 PowerShell 脚本有执行策略限制。以下操作被 blocked by policy:

被阻止的操作 错误信息 影响
Copy-Item 私钥到 ~/.ssh Access to the path is denied 密钥无法复制到标准位置
icacls 搭配变量/管道的复合命令 blocked by policy 权限修复脚本无法执行
含复杂引号的长 SSH 命令 outputFormat parameter error -o 参数被误解为 cmdlet 参数

绕过方案:环境权限策略放宽后,直接对原密钥文件路径执行 icacls,分步执行单条命令,不使用变量拼接或复杂管道。

3.2 第二层:私钥权限过宽(最根本的阻断)

即使 TCP 连接成功(Verbose 日志显示 Connection established、SSH2_MSG_NEWKEYS received),OpenSSH 客户端也会在私钥加载阶段拒绝使用:



text

Load key "D:\...\private_key": bad permissions
user@[SERVER IP]: Permission denied (publickey,password).

Verbose 关键行:



text

debug1: identity file D:\...\private_key type -1

type -1 表示 SSH 客户端认为文件格式无效或无法访问,原因不是文件内容损坏,而是权限检查未通过,导致客户端直接跳过该密钥。

权限问题根源:私钥位于 OneDrive 同步目录,OneDrive 默认同步机制赋予了 BUILTIN\Users(读取执行)和 NT AUTHORITY\Authenticated Users(修改)权限。Windows OpenSSH 一旦检测到"其他用户"能读取私钥,就拒绝使用。

修复前后对比:



text

修复前(SSH 拒绝加载):
  [SANDBOX USER]:(I)(M,DC)         ← Codex 沙箱用户有修改权
  BUILTIN\Users:(I)(RX)                    ← 所有用户可读
  NT AUTHORITY\Authenticated Users:(I)(M)  ← 验证用户可修改

修复后(SSH 成功加载):
  NT AUTHORITY\SYSTEM:(F)                  ← SYSTEM 完全控制
  BUILTIN\Administrators:(F)               ← 管理员完全控制
  [USER]:(R)                              ← 当前用户只读

/deny 误用(额外踩坑):第一次收紧权限时用了 /deny 而非 /remove,导致 ACL 中出现 DENY 条目。DENY 优先级高于 ALLOW,虽然显式为当前用户加了 (R),但 Authenticated Users:(DENY)(M) 仍然阻止了访问。必须改用 /remove 彻底移除条目。

3.3 第三层:TCP 端口探测误判

Test-NetConnection 对 22 端口返回超时(TcpTestSucceeded : False),但实际 ssh -v 发现 SSH 客户端在约 3 秒内完成了 TCP 握手。

原因:

  • Test-NetConnection 的默认超时策略与 SSH 客户端不同。SSH 的 -o ConnectTimeout=10 参数可以更早报告失败,但在连接成功的情况下会在认证阶段等待更久(默认 30-60 秒)
  • PowerShell 的 Test-NetConnection 对某些端口(非标准 SSH 22)的探测可能受防火墙策略影响而丢弃 SYN 包,而 SSH 客户端经过加密握手后协议层面的连通性更好
  • 实际链路正常,ICMP ping 也可达(191ms),但 TCP SYN 探测被中间设备选择性丢弃

教训:不要以 Test-NetConnection 的结果作为判断 SSH 可否连接的唯一依据。ssh -v 的握手过程输出了更真实的链路状态。

3.4 完整故障树



text

SSH 连接失败
│
├── TCP 层 [部分连通]
│   ├── Test-NetConnection 超时 → 误判为网络不通
│   ├── ssh -v 实际 3 秒内完成 TCP 建连
│   └── 结论:端口探测工具不可靠,以 ssh -v 为准
│
├── 密钥认证层 [阻塞]
│   ├── Load key: bad permissions / type -1
│   ├── 原因:OneDrive 引入多余权限(Users、Authenticated Users)
│   ├── 误试:用 /deny 加拒绝规则,DENY 仍阻断当前用户
│   ├── 修复:/remove 彻底删除 + /inheritance:r 取消继承
│   └── 验证:icacls 确认仅 SYSTEM/Administrators/当前用户
│
└── 执行策略层 [Codex 环境屏障]
    ├── PowerShell 策略拦截 Copy-Item / 复合 icacls
    ├── 长命令被误解析(-o 参数冲突)
    └── 解决:环境权限放宽 + 分步执行简单命令

4. 备选连接方式

4.1 Python Paramiko(不依赖文件权限检查)



python

import paramiko

key = paramiko.Ed25519Key.from_private_key_file("D:\\path\\to\\private_key")
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
    "[SERVER IP]", port=22,
    username="root", pkey=key, timeout=15
)

stdin, stdout, stderr = client.exec_command("hostname")
print(stdout.read().decode())
client.close()

优点:Paramiko 不检查 Windows 文件 ACL,可在密钥权限过宽时正常工作。 局限:

  • 需要 pip install paramiko
  • 实际测试中 Paramiko 的 Ed25519 密钥解析曾报 SSHException: Invalid key,说明对密钥文件格式有一定要求(可能缺失换行符或包含 BOM)

4.2 SCP 文件传输



powershell

scp -P 22 `
    -o StrictHostKeyChecking=no `
    -i "D:\path\to\private_key" `
    local_file.txt user@[SERVER IP]:/destination/path/

5. 经验总结

  1. 先查密钥权限 —— icacls 查看文件 ACL,确保当前用户外无其他用户可读
  2. /remove 不是 /deny —— 用 /remove 删条目,不用 /deny 加拒绝,否则 DENY 仍阻断当前用户
  3. 不要盲信 Test-NetConnection —— 超时不代表 SSH 一定连不上,ssh -v 才是真实依据
  4. type -1 ≠ 文件损坏 —— 优先检查权限,不是重新生成密钥
  5. Verbose 是黄金诊断工具 —— ssh -v 输出密钥加载、握手过程、认证方法序列,直接定位阻塞层
  6. 沙箱环境有策略限制 —— Codex CLI 可能拦截复合 PowerShell 命令,必要时分步执行单条原始命令
  7. OneDrive 目录下的密钥有额外风险 —— 同步机制会带入 Authenticated Users 权限,需手动收紧

文档版本:v1.1 | 最后更新:2026-06-20 | 适用环境:Windows 11 + PowerShell 7 / Codex CLI

Leave a Comment