ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Mac免密登录Windows实战:OpenSSH权限与隐藏坑全解析

Mac免密登录Windows实战:OpenSSH权限与隐藏坑全解析 说实话我一开始真没把这当回事。毕竟在群晖上SSH 免密登录就是一锤子买卖生成密钥、把公钥塞进 authorized_keys、改一下权限完事。趁着那股顺手劲儿我想着把 Mac 免密登录 Windows 也一块儿配了结果这一配就是整整一个晚上。从 SecureCRT 切到原生 ssh 命令的念头差点就在这场折腾里被劝退。今天把这段经历完整记录下来。不是什么高深理论全是实际操作中一点一点踩出来的细节。如果你也打算在 Mac 上用 SSH 免密登录 Windows尤其是想把群晖上已经验证过的“密钥套路”直接复制到 Windows 上这篇文章应该能帮你少走一大半弯路。1. 触发这趟折腾的真实场景从群晖到 Windows 的思维惯性先说背景。我在群晖上早就配好了 SSH 密钥日常备份、拉日志、跑脚本都是免密进出体验非常顺畅。于是当家里那台 Windows 机器也需要频繁远程操作时我脑子里冒出来的第一个方案就是“把群晖上那套搬过来不就行了”1.1 原本的“复刻”计划在群晖上SSH 免密登录的流程极其标准在客户端Mac生成密钥对把公钥内容追加到群晖的~/.ssh/authorized_keys确保.ssh目录是 700、authorized_keys 是 600完事直接ssh user群晖IP进系统。这套流程我闭着眼都能操作。所以当面对 Windows 时我心里想的是Windows 上装个 OpenSSH 服务端把公钥放过去改一下权限理论上应该是一样的。但现实很快教会我一个道理——Windows 的权限模型和 Linux 完全不是一回事尤其是 OpenSSH for Windows 对密钥文件权限的校验逻辑比 Linux 上要敏感得多。1.2 Windows 上 SSH 服务端选型OpenSSH 与 Bitvise 怎么选在动手之前我先在 Windows 端做了一个服务端选型。目前主流的方案有两个对比项OpenSSH for WindowsBitvise SSH Server安装方式Windows 可选功能 / PowerShell 命令独立安装包带图形化管理界面配置文件C:\ProgramData\ssh\sshd_config自带虚拟账户管理无需手写配置密钥文件位置用户目录 .ssh\authorized_keys 或 administrators_authorized_keys图形界面里直接粘贴公钥权限校验严格ACL 权限不对会被拒绝相对宽松灵活度高但坑多低但上手容易适合人群习惯命令行、愿意排查细节的希望开箱即用、不想折腾的我当时选择的是 OpenSSH for Windows因为它是系统自带能力不需要额外安装第三方软件而且我平时习惯用命令行操作。事后证明这个选择让我把 Windows OpenSSH 的各种隐藏机制都摸了一遍。如果你不想折腾直接用 Bitvise 会简单很多但代价是少学到不少底层细节。2. 一把按 Linux 经验照搬的“标准配置”为什么看着没问题却不生效既然选定了 OpenSSH for Windows我立刻按照“群晖经验”开始操作。这个阶段我完全没有意识到后面所有的坑都埋在这些看似正常的步骤里。2.1 Mac 端密钥生成与常见权限问题Mac 端的密钥生成很常规。我习惯用 ed25519 算法文件单独命名方便多台机器管理ssh-keygen -t ed25519 -C mac-to-windows -f ~/.ssh/windows_box这里有个容易忽略的点Mac 端私钥文件的权限必须是 600。如果你是从群晖上把私钥文件拷贝到 Mac 的拷贝之后的权限往往会是 644SSH 客户端会直接拒绝使用这个私钥并报Permissions 0644 for xxx are too open。处理方式很简单chmod 600 ~/.ssh/windows_box这一步做完Mac 端的准备就算完成了。2.2 把公钥部署到 Windows 账户目录接下来是把公钥内容放到 Windows 上。这里我踩了第一个小坑Windows 上默认没有.ssh目录需要手动创建而且如果用资源管理器创建目录再去用记事本编辑文件很容易引入编码问题。我当时是直接在 PowerShell 里操作的New-Item -ItemType Directory -Path $env:USERPROFILE\.ssh -Force Set-Content -Path $env:USERPROFILE\.ssh\authorized_keys -Value ssh-ed25519 AAAA... youremail -Encoding ascii注意最后面那个-Encoding ascii参数。PowerShell 5.1 的Set-Content默认编码是 Unicode如果直接用默认参数写入authorized_keys 文件会带 BOM 头OpenSSH 在解析公钥时可能直接失败。我见过有人在网上问“公钥明明粘贴了为啥还是提示 No supported authentication methods available”十有八九就是编码问题。2.3 配置 sshd_config 与重启服务公钥放好后我检查了C:\ProgramData\ssh\sshd_config里的几个关键配置项PubkeyAuthentication yes PasswordAuthentication yes AuthorizedKeysFile .ssh/authorized_keysPubkeyAuthentication必须为 yes这个是公钥认证的开关PasswordAuthentication在调试期间先保持 yes方便出问题时还能用密码登录排查等完全调通了再关掉。修改完配置后在管理员 PowerShell 里重启服务Restart-Service sshd一切看起来都非常标准。然后我从 Mac 上发起连接ssh -i ~/.ssh/windows_box windows用户名Windows主机IP结果对方依然淡定地提示我输入密码。那一刻我的内心是崩溃的。2.4 第一次失败的现场记录第一次失败的现场记录现在回过头看非常有价值客户端行为没有报错没有拒绝就是单纯地要密码。服务端表现sshd 服务正常系统事件日志里看不到明显异常。配置文件检查PubkeyAuthentication 确实是 yes。公钥内容检查和 Mac 端公钥文件一字不差。从表面看所有环节都正确但免密就是不生效。这正是最让人抓狂的地方系统没有给你一个“配置错误”的明确提示而是用一种沉默的方式拒绝你的公钥。要找到问题只能一层一层往下挖。3. 坑一Windows 的 ACL 权限模型才是免密登录的头号杀手在 Linux 上密钥文件的权限问题是最好解决的chmod 600 authorized_keys一行命令就搞定。但 Windows 上完全不是这么回事。3.1 症状客户端反复要求密码服务端日志只有一行拒绝我先说症状。当你发现客户端一直在要密码而你的公钥明明已经放进 authorized_keys 时不要急着怀疑密钥内容。打开 Windows 的事件查看器路径是应用程序和服务日志 - Microsoft - Windows - OpenSSH - Operational在这里能看到 sshd 的详细日志。我当时看到的关键一行是Authentication refused: bad ownership or modes for file C:\Users\用户名\.ssh\authorized_keys这句日志翻译过来就是authorized_keys 文件的“所有者”或“权限模式”不对服务端拒绝使用这个文件。也就是说公钥本身没问题问题出在系统认为这个文件不够安全不敢信任它。3.2 Windows OpenSSH 对密钥文件的权限校验逻辑为什么 Windows 这么严格因为 OpenSSH 本身源自 Linux它的权限安全模型是基于 POSIX 的“属主 权限位”设计的。到了 Windows 上NTFS 的 ACL 权限模型比 POSIX 复杂得多系统没法简单地把权限位映射过来所以官方干脆做了一个很严格的策略authorized_keys 文件只能被当前登录用户和 SYSTEM 账户完全控制其他任何用户包括 Administrators 组、Users 组、Everyone都不能对该文件有任何写权限。如果文件的 ACL 里存在哪怕一个“其他用户可写”的条目sshd 就会认为这个文件不安全直接放弃使用它然后退回密码认证。这里可以用一个生活化的类比来理解你有一张门禁卡卡上写着“仅限本人使用”但门禁系统的规则是——如果这张卡上还印着“其他任何人都有权修改这张卡”系统就认定卡不可信直接拒绝放行。Windows 上的 OpenSSH 就是这个逻辑。3.3 用 icacls 手工收拢权限命令详解知道了问题解决起来就有方向了。在 Windows 上修改 ACL 权限最常用的命令是icacls。首先对 authorized_keys 文件本身收权限icacls $env:USERPROFILE\.ssh\authorized_keys /inheritance:r /grant $env:USERNAME:F /grant SYSTEM:F拆分解释一下/inheritance:r移除所有从父目录继承的权限条目。这个参数非常关键因为 Windows 默认的目录权限是带继承的继承结果里会包含 Users 组、Everyone 等条目这些都有写权限必须全部清掉。/grant $env:USERNAME:F给当前用户完全控制权限。/grant SYSTEM:F给 SYSTEM 账户完全控制权限。光设置文件还不够.ssh目录本身也要做同样的处理icacls $env:USERPROFILE\.ssh /inheritance:r /grant $env:USERNAME:F /grant SYSTEM:F目录的权限同样重要如果目录可以被其他用户写入攻击者就可以替换掉里面的 authorized_keys 文件相当于把你的门禁卡换成他的所以 sshd 对目录权限同样敏感。3.4 为什么“复制 Linux 目录结构”在 Windows 上会出事这里我多说一句为什么“照搬 Linux 经验”在 Windows 上会栽跟头。在 Linux 上权限就是简单的 rwx 三位chmod 600 干净利落。但 Windows 的 NTFS 权限是一整套 ACL每个文件可以有多条访问控制条目而且默认情况下所有文件都会从父目录继承 permissions。当你用 scp、U盘或者其他方式把群晖上的.ssh目录整体复制到 Windows 时这些文件会继承 Windows 目标目录的 ACL。典型的结果是文件里包含了Users组的读写权限、Everyone组的读取权限甚至Authenticated Users的权限条目。这些在多用户系统里看起来“正常”的权限在 OpenSSH 眼里全是漏洞直接拒绝信任。所以无论你从哪拿到 authorized_keys 文件到了 Windows 上都必须重新用 icacls 收拢权限这一步省不了。4. 坑二管理员账户与 OpenSSH 的授权文件重定向权限修完之后我满怀信心地又试了一次结果还是不行。这次我仔细观察日志发现它连 authorized_keys 文件都不读了。这个坑比权限问题更隐蔽。4.1 现象改了 authorized_keys 还是不行权限已经是标准的“当前用户 SYSTEM”文件位置也是默认的C:\Users\用户名\.ssh\authorized_keys公钥内容确认无误但 SSH 就是不用公钥认证。更诡异的是日志里连“bad ownership or modes”这种提示都消失了像是 sshd 压根就没看过这个文件。这个现象说明问题不在文件本身而在服务端的逻辑sshd 可能根本没有去用户目录下找 authorized_keys。4.2 administrators_authorized_keys 的特殊逻辑答案在 Windows OpenSSH 的一个特殊设计里如果登录用户属于本地管理员组sshd 默认不会读取用户目录下的 authorized_keys而是去读C:\ProgramData\ssh\administrators_authorized_keys这个文件。这是 Windows OpenSSH 为了避免权限提升风险特意做的处理管理员用户本身权限太大如果还允许普通路径下的 authorized_keys 生效一旦密钥泄露影响面会更大。所以它把管理员用户的公钥验证单独放到了一个受保护的系统目录里。我当时登录的 Windows 账户正好是管理员组成员所以无论我怎么调整用户目录下的 authorized_keys服务端都不理会。解决方案是把公钥写入C:\ProgramData\ssh\administrators_authorized_keys并且同样严格收权$adminKeyFile $env:ProgramData\ssh\administrators_authorized_keys Set-Content -Path $adminKeyFile -Value ssh-ed25519 AAAA... youremail -Encoding ascii icacls $adminKeyFile /inheritance:r /grant SYSTEM:F /grant Administrators:F这里注意授权对象是Administrators组而不是当前用户名因为 sshd 读取这个文件时是以服务身份SYSTEM访问的而验证的文件属主语义与普通用户目录不同。4.3 两种用户的差异对比为了让你一眼看明白我把区别整理成了表格登录账户类型公钥文件位置权限要求普通用户C:\Users\用户名.ssh\authorized_keys当前用户 SYSTEM 完全控制管理员组成员C:\ProgramData\ssh\administrators_authorized_keysSYSTEM Administrators 完全控制判断当前用户是否为管理员组成员可以在 PowerShell 里执行net localgroup administrators | Select-String -SimpleMatch $env:USERNAME如果有输出说明当前用户是管理员那就要走administrators_authorized_keys这条路。4.4 Bitvise 场景下的差异如果你选择的是 Bitvise SSH Server情况会简单很多。Bitvise 有图形化管理界面直接在用户配置里添加公钥即可不需要关心 authorized_keys 权限也不存在管理员账户重定向的问题。但 Bitvise 也有它自己的坑它默认使用的是系统账户还是虚拟账户、公钥粘贴时的格式、密钥算法支持等都需要在界面上确认。尤其是如果你同时装了 OpenSSH for Windows 和 Bitvise两者会抢占 22 端口造成服务起不来。我当时为了排查问题同时装了两个结果两个服务反复冲突最后只能卸掉 Bitvise专心搞 OpenSSH。5. 从 ssh -vvv 到事件查看器完整排查链路复盘如果你现在也卡在“配置正确但免密不生效”的状态不要慌我总结了一套从客户端到服务端的完整排查链路。这套链路不仅适用于 Mac 登录 Windows也适用于任何 SSH 免密认证失败的问题。5.1 第一步客户端开启 Verbose 模式定位认证阶段客户端这边最直接的定位方式就是用-vvv参数让 SSH 输出详细的调试信息ssh -vvv -i ~/.ssh/windows_box windows用户名Windows主机IP输出里需要重点关注的内容debug1: Authentications that can continue: publickey,password说明服务器允许公钥认证和密码认证两种方式。如果这里没有publickey问题就出在服务端配置上。debug1: Offering public key: ...客户端正在尝试提交公钥。debug1: Server accepts key服务端接受了公钥接下来应该直接登录成功。如果看不到Server accepts key而是反复出现Authentications that can continue: publickey,password说明服务端拒绝了公钥认证需要去服务端日志里找原因。通过-vvv能快速判断问题是在客户端提交阶段还是服务端校验阶段缩小排查范围。5.2 第二步翻服务端日志定位被拒原因Windows 上 OpenSSH 的日志默认写到事件查看器里路径我前面说过应用程序和服务日志 - Microsoft - Windows - OpenSSH - Operational这里有 sshd 的详细认证记录。最常见的有几条Authentication refused: bad ownership or modes for file ...权限问题按上一章的方法用 icacls 处理。Failed publickey for ...公钥校验失败可能是公钥内容不对、密钥算法不受支持或者文件编码有问题。没有任何与公钥相关的日志大概率是服务端根本没去读你预期的那个文件检查是不是管理员账户重定向到了administrators_authorized_keys。事件查看器的好处是它会把 sshd 的真实想法告诉你。我在排错过程中几乎所有关键线索都是从这里拿到的。5.3 第三步检查账户状态与本地安全策略如果日志里显示公钥校验通过但登录还是失败或者干脆没有任何认证记录就要把目光从 SSH 本身移开看看 Windows 账户层面的问题。一个很容易踩的坑是Windows 账户没有设置密码。Windows 默认有一条本地安全策略——“账户: 使用空白密码的本地账户只允许进行控制台登录”默认是启用的。也就是说如果被登录的 Windows 账户密码为空任何远程登录方式包括 SSH都会被拒绝只有坐在电脑前才能登录。解决方式是给账户设一个密码或者手动修改这条安全策略不建议关掉风险太大。另一个问题是用不到 1 的账户可能被禁用了比如net user 用户名查看账户状态确认不是Disabled。5.4 第四步密钥格式与换行符的隐蔽问题密钥本身的格式问题也是排查中容易忽略的盲区。第一个是密钥算法。如果你的 Mac 系统版本较老或者 Windows 端的 OpenSSH 版本较老ed25519 可能不被支持。遇到这种情况可以换用 RSA 密钥重新生成ssh-keygen -t rsa -b 4096 -C mac-to-windows-rsa -f ~/.ssh/windows_rsa第二个是文件编码和换行符。我在前面提到过PowerShell 5.1 的默认编码会带 BOM可能影响公钥解析。更稳妥的做法是在 PowerShell 里用 ASCII 编码写入或者用记事本打开公钥文件、全选复制、再粘贴到 Windows 的目标文件中保存为 ANSI 编码。第三个是公钥内容完整性。复制粘贴过程中公钥很容易被截断尤其是前面那段ssh-ed25519 AAAA...的 base64 部分只要少一个字符服务端就会校验失败。我习惯在 Windows 上用命令验证一下Get-Content $env:USERPROFILE\.ssh\authorized_keys | Format-List确认内容只有一行且以ssh-ed25519或ssh-rsa开头。6. 最终修复脚本与日常使用建议排查到这里问题已经全部定位清楚了。我把整个修复过程整理成了一个 PowerShell 脚本直接在 Windows 上用管理员身份运行就能完成所有关键配置。6.1 一键修复脚本PowerShell# 以管理员身份运行 $username $env:USERNAME $sshDir $env:USERPROFILE\.ssh $authKeys $sshDir\authorized_keys # 1. 创建 .ssh 目录 New-Item -ItemType Directory -Path $sshDir -Force | Out-Null # 2. 写入公钥请替换成你自己的公钥内容 $publicKey ssh-ed25519 AAAA... youremail if (-not (Test-Path $authKeys)) { Set-Content -Path $authKeys -Value $publicKey -Encoding ascii } # 3. 收拢 .ssh 目录和 authorized_keys 文件的权限 icacls $sshDir /inheritance:r /grant $username:F /grant SYSTEM:F icacls $authKeys /inheritance:r /grant $username:F /grant SYSTEM:F # 4. 如果当前用户是管理员组成员处理 administrators_authorized_keys if (net localgroup administrators | Select-String -SimpleMatch $username) { $adminKeyFile $env:ProgramData\ssh\administrators_authorized_keys Set-Content -Path $adminKeyFile -Value $publicKey -Encoding ascii icacls $adminKeyFile /inheritance:r /grant SYSTEM:F /grant Administrators:F } # 5. 确保 sshd_config 开启公钥认证 $conf $env:ProgramData\ssh\sshd_config (Get-Content $conf) -replace ^#PubkeyAuthentication.*, PubkeyAuthentication yes | Set-Content $conf -Encoding ascii # 6. 重启 sshd 服务 Restart-Service sshd脚本的思路很直接先创建目录、写入公钥然后对可能影响认证的每个文件都做了严格的 ACL 收权最后确认服务端配置并重启。如果你已经手动修改过部分配置脚本也能幂等执行不会破坏已有设置。6.2 验证免密登录是否真正生效脚本执行完毕回到 Mac 上验证ssh -i ~/.ssh/windows_box windows用户名Windows主机IP如果一切正常这次应该直接进入 Windows 的 PowerShell 界面不再询问密码。我建议再进一步在 Mac 的~/.ssh/config里加一段配置省去每次都要带参数的麻烦Host win HostName 192.168.1.100 User yourname IdentityFile ~/.ssh/windows_box PreferredAuthentications publickey之后直接ssh win就能登录非常顺手。6.3 几点后续避坑经验免密登录跑通之后我总结了几个后续使用中容易再次踩坑的点。第一个是重启之后 sshd 服务可能没起来。Windows 的 OpenSSH 服务默认是手动启动如果你用的不是最新版本系统或者安装方式不对重启之后服务可能处于停止状态。最好把服务设为自动启动Set-Service -Name sshd -StartupType Automatic第二个是调试完成后建议关闭密码认证。将 sshd_config 里的PasswordAuthentication改为no避免有人暴力破解密码。改完之后记得重启服务Restart-Service sshd第三个问题是如果你在 Mac 上换了系统或者重置了钥匙串私钥可能被系统拒绝。重新设置权限即可chmod 600 ~/.ssh/windows_box第四个问题涉及多台设备。如果后来你在群晖或其他机器上生成了新密钥别忘记 Windows 端需要重新添加公钥并再次收权。我后来就吃过一次亏先在群晖上配了新密钥Windows 端忘了更新 administrators_authorized_keys结果又排查了半天。最后再分享一个小技巧如果某一天突然免密登录失败先别急着改配置直接看 Windows 事件查看器里 OpenSSH 的 Operational 日志。日志会直接告诉你文件权限有问题还是账户有问题。大多数时候问题都能在一分钟内定位。这趟折腾让我养成了一个习惯——在 Windows 上配置任何 SSH 相关的东西第一件事不是看文件内容而是看权限。权限对了事情就成了一大半。
返回列表