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