在 Windows 上配置 SSH Key 认证(只允许 Key 登录)

这篇文章记录了在 Windows 上配置 SSH key 认证的完整过程。作为一个从 Linux 过来的用户,Windows 的 OpenSSH 和 Linux 行为有几个很坑的差异,踩坑之后特此记录…

整体目标

从 Ubuntu(或其他远程主机)通过 SSH key 连接到 Windows 机器,不使用密码。具体要求:

  • Windows 开启 OpenSSH Server
  • 自定义端口(本文以 2333 为例)
  • 禁用密码登录,只允许 SSH key
  • 路由器配置端口转发(家用网络必须)

第一步:安装 OpenSSH Server

首先,用管理员身份打开 PowerShell(右键 → 以管理员身份运行),然后检查是否已安装:

Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Server*'

⚠️ 注意:这个命令必须用管理员权限运行,普通用户会报”请求的操作需要提升”的错误。

如果显示 State : NotPresent,执行安装:

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

安装完成后确认状态变为 State : Installed。安装过程大概需要5到15分钟, 或更多不等,请耐心等待。

如果命令行安装卡住,也可以通过图形界面安装:设置 → 应用 → 可选功能 → 添加功能 → 搜索 “OpenSSH Server”


第二步:启动 SSH 服务

Start-Service sshd
Set-Service -Name sshd -StartupType Automatic

服务启动后,配置文件才会生成。注意 C:\ProgramData隐藏目录,在文件管理器地址栏直接输入路径 C:\ProgramData\ssh 回车即可进入。


第三步:修改 SSH 配置

编辑配置文件(需要管理员权限):

C:\ProgramData\ssh\sshd_config

添加或修改以下内容:

Port 2333
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
AuthorizedKeysFile .ssh/authorized_keys

建议删除重复的 AuthorizedKeysFile 行(Windows 默认配置文件里有时会有两行)。


第四步:放行防火墙端口

Windows 不会自动为新端口添加防火墙规则,需要手动添加:

New-NetFirewallRule -Name sshd-2333 `
  -DisplayName "OpenSSH Server (2333)" `
  -Enabled True `
  -Direction Inbound `
  -Protocol TCP `
  -Action Allow `
  -LocalPort 2333

第五步:添加公钥

生成 SSH key(在 Ubuntu/客户端机器上)

如果还没有 SSH key:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/kth_id_rsa

查看公钥内容:

cat ~/.ssh/kth_id_rsa.pub

把公钥放到 Windows

这里有一个 Windows OpenSSH 特有的大坑,和 Linux 不同:

如果你的 Windows 用户属于 Administrators 组(即使是 deny-only),OpenSSH 不会读取 C:\Users\<用户名>\.ssh\authorized_keys,而是优先读取:

C:\ProgramData\ssh\administrators_authorized_keys

你可以通过以下命令确认自己是否在 Administrators 组:

whoami /groups

如果看到 BUILTIN\Administrators(无论什么属性),建议把公钥放到这个路径:

C:\ProgramData\ssh\administrators_authorized_keys

把 Ubuntu 上的公钥内容(kth_id_rsa.pub 的内容,一整行)粘贴进这个文件。

设置权限(非常关键)

Windows OpenSSH 对文件权限非常严格,权限不对 key 会被直接忽略:

# 针对管理员的 authorized_keys 文件
icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r
icacls C:\ProgramData\ssh\administrators_authorized_keys /grant SYSTEM:F
icacls C:\ProgramData\ssh\administrators_authorized_keys /grant Administrators:F

如果你是普通用户(不在 Administrators 组),文件放 C:\Users\你的用户名\.ssh\authorized_keys,权限这样设:

icacls C:\Users\Kin\.ssh /inheritance:r
icacls C:\Users\Kin\.ssh /grant Kin:F
icacls C:\Users\Kin\.ssh /grant SYSTEM:F

icacls C:\Users\Kin\.ssh\authorized_keys /inheritance:r
icacls C:\Users\Kin\.ssh\authorized_keys /grant Kin:F
icacls C:\Users\Kin\.ssh\authorized_keys /grant SYSTEM:F

⚠️ 修改 .ssh 目录权限时要小心,权限改坏了会导致你连”ssh 出去”都失败。如果出问题,用 icacls ... /reset 重置。


第六步:重启 SSH 服务

Restart-Service sshd

第七步:路由器端口转发(家用网络必须)

如果你的 Windows 机器在家用网络下(IP 类似 192.168.x.x),公网 IP 属于路由器,外部连接会被路由器挡住。

检查方法:

netstat -ano | findstr 2333

如果看到类似 192.168.1.100:2333,说明是内网 IP,需要在路由器后台(通常是 192.168.1.1)添加端口转发规则:

外部端口 内网 IP 内网端口 协议
2333 192.168.1.100 2333 TCP

Check a detailed example in my other blog post.


第八步:测试连接

在 Ubuntu(客户端)上测试:

ssh -p 2333 -i ~/.ssh/kth_id_rsa Kin@你的公网IP

成功的话应该直接进入 Windows 的 shell,不会提示输入密码。

为了方便,可以在 Ubuntu 的 ~/.ssh/config 里配置别名:

Host win
    HostName 你的公网IP
    Port 2333
    User Kin
    IdentityFile ~/.ssh/kth_id_rsa

之后直接 ssh win 即可。


常见问题排查

问题 1:连接卡住(Connecting to … port 2333,无后续)

说明端口没通,不是认证问题。检查:

  1. Windows 防火墙是否放行了 2333
  2. 路由器是否配置了端口转发
  3. netstat -ano | findstr 2333 确认 sshd 确实在监听

问题 2:Permission denied (publickey)

说明 key 发出去了,但服务器拒绝了。检查:

  1. 公钥是否放到了正确路径(见上文关于管理员路径的说明)
  2. 文件权限是否正确(icacls 设置)
  3. authorized_keys 文件内容是否完整(一整行,不能断行)
  4. 文件编码是否正确(用记事本保存可能引入 BOM)——建议用 PowerShell 写入:
(Get-Content C:\ProgramData\ssh\administrators_authorized_keys) | Set-Content -Encoding ascii C:\ProgramData\ssh\administrators_authorized_keys
  1. 查看 OpenSSH 日志,定位具体原因:
Get-WinEvent -LogName "OpenSSH/Operational" -MaxEvents 20

问题 3:Connection closed by authenticating user [preauth]

这通常说明 sshd 根本没有进入验证密钥的阶段。最常见原因是公钥放错了路径——请确认你的用户是否在 Administrators 组,然后把公钥放到对应的正确路径。

问题 4:修改了 .ssh 权限后,自己连不上其他主机了

.ssh 目录不只是服务端用,SSH 客户端也依赖里面的 id_rsaknown_hosts 等文件。权限过严会导致客户端也无法使用 key。恢复方法:

icacls C:\Users\Kin\.ssh /reset
icacls C:\Users\Kin\.ssh /grant Kin:F
icacls C:\Users\Kin\.ssh /grant SYSTEM:F
icacls C:\Users\Kin\.ssh /grant Administrators:F

SSH 端口转发(-L)的一个经典坑

如果你用 SSH 端口转发(如访问 Windows 上的某个服务),命令写法很重要:

有问题的写法:

ssh -L 2053:localhost:2053 -p 2333 Kin@你的IP

推荐写法:

ssh -L 2053:127.0.0.1:2053 -p 2333 Kin@你的IP

原因:在 Windows 上,localhost 可能被解析为 IPv6 的 ::1,而很多服务只监听 IPv4 的 127.0.0.1,导致端口转发建立了但访问不通。显式写 127.0.0.1 可以绕过这个问题。

验证方法:在 Windows 上 ping localhost,如果结果是 ::1 就说明默认走 IPv6。


Windows SSH 与 Linux 的关键区别总结

项目 Linux Windows
authorized_keys 路径(普通用户) ~/.ssh/authorized_keys C:\Users\<user>\.ssh\authorized_keys
authorized_keys 路径(管理员用户) 同上 C:\ProgramData\ssh\administrators_authorized_keys
权限要求 宽松 严格,权限不对直接拒绝
localhost 解析 通常是 IPv4 可能是 IPv6(::1
配置文件 /etc/ssh/sshd_config C:\ProgramData\ssh\sshd_config

最终安全配置建议

Port 2333
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
AllowUsers Kin
AuthorizedKeysFile .ssh/authorized_keys

这套配置禁用密码、限制尝试次数、只允许指定用户登录,已经是比较安全的基础配置了。




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Ansible 多机自动化工具 初学笔记 - Kin_Zhang - 博客园
  • Claude Code/Cursor 个人配置比较
  • 使用 Docker 部署 3x-ui 并配置 VPN 节点 使用新ip
  • UE4/CARLA 启动崩溃排查 | Fix Vulkan Segmentation Fault on Linux
  • 🐧 Ubuntu 中文输入法配置指南(20.04 & 22+ / Debian)