☰
Windows OpenSSH安装与配置:服务端、免密登录及排障实战
2026/10/2 19:26:07 网站建设 项目流程

1. 从一台没有图形界面的Windows Server说起:装OpenSSH到底要解决什么

几年前接手过一批运维任务,其中一台是跑在机房里的Windows Server,没有图形界面,日常只能靠远程桌面。问题在于,业务脚本部署在另一台Linux跳板机上,需要定期把生成的报表目录同步过去。RDP传文件慢、断线重连还容易中断,最靠谱的做法其实是让这台Windows也能被ssh/scp直接访问。于是就有了这篇内容要聊的事:在Windows上安装OpenSSH,把服务端和客户端都配起来。

OpenSSH在Windows上早已不是外挂级别的存在。从Windows 10 1809和Windows Server 2019开始,微软把OpenSSH客户端做成了系统内置组件,服务端则以"可选功能"的形式提供。这意味着大部分情况下你根本不需要去第三方站点下载什么安装包,动动命令就能装上。但真到了实操环节,坑还是不少:有的机器装完服务起不来,有的连上了却被立刻踢掉,有的密钥怎么放都不生效。

这篇内容适合三类人:需要在Windows上开sshd做远程运维的工程师、要写自动化脚本调用ssh/scp的开发、以及接手别人配好的Windows机器但看不懂配置的维护者。下面我会把在线安装、离线部署、配置调整、免密登录和排障这几块拆开讲,每一条都尽量说清楚"为什么这么做",而不是只丢一串命令。

提示:Windows上的OpenSSH分为客户端和服务端两个独立组件,装了客户端不代表能当服务器用,这是最常见的认知偏差,后面会单独展开。

2. 动手前先摸清底数:客户端和服务端各在不在

2.1 一条命令看清两个组件的安装状态

很多人一上来就敲ssh命令,发现能用,就以为服务端也装好了。实际上内置的那份只是客户端,用来从Windows连别人;要让别人连进这台Windows,得额外装OpenSSH Server。判断方法很简单,用管理员权限打开PowerShell,执行:

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

输出里会出现两行,形如OpenSSH.Client~~~~0.0.1.0和OpenSSH.Server~~~~0.0.1.0,后面的State字段会显示Installed或NotPresent。这一步的价值在于:先确定你要装的是哪一个,避免装完发现方向错了。我见过有人折腾半天服务端,结果他的需求只是从Windows连Linux,装个客户端就够了。

顺便再确认一下版本:

ssh -V

这条命令会打印类似OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.2的信息。记下这个版本号,后面排查兼容性问题时要用到。

2.2 Windows版本与OpenSSH能力的对应关系

不同Windows版本能拿到的OpenSSH版本差别不小,这直接影响你能用哪些特性。比如较老的Windows Server 2016,系统自带的可选功能里根本没有OpenSSH条目,只能走离线安装那条路。而Windows 10 1809之后的版本,内置的客户端版本会随系统更新一起走。

系统版本内置客户端可选功能服务端建议做法
Windows 10 1809+有有直接在线安装
Windows Server 2019+有有直接在线安装
Windows Server 2016无无离线部署Win32-OpenSSH
Windows 7 / 2008 R2无无离线部署,且要注意API兼容

注意:Windows Server 2016这类系统里,即便你手动补了OpenSSH的二进制文件,也可能因为缺少某些系统调用而出现服务启动失败。这种时候优先考虑升级系统或用文件同步类工具替代,不要硬扛。

这里补一个经验:如果你拿到的是一台加固过的机器,可选功能列表可能是被策略裁剪过的,Get-WindowsCapability返回空或者报错。遇到这种情况,直接跳到离线安装那一节,别在在线路径上耗时间。

3. 在线安装:从可选功能到服务真正跑起来

3.1 图形界面路径适合偶尔用一次的人

路径是"设置 → 应用 → 可选功能 → 添加功能",在搜索框里输入OpenSSH,会看到"OpenSSH 客户端"和"OpenSSH 服务器"两个条目,选中服务器那个点安装即可。整个过程一两分钟,适合只是临时搭个环境、不想记命令的人。缺点是图形界面在Server Core版本上是没有的,而且批量部署时效率太低。

3.2 PowerShell命令才是批量部署的正解

实际工作中我更推荐命令行,一是可复制、可写进脚本,二是能明确知道每一步的结果:

# 安装服务端 Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 # 安装客户端(如果确实没装) Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

执行完看到Online : True、RestartNeeded : False就算成功了。如果安装中途失败,或者状态卡在InstallPending,可以用修复命令重置一下:

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

需要卸载时把动词换成Remove-WindowsCapability即可,注意卸载服务端会一并移除C:\ProgramData\ssh下的配置,操作前先备份。

3.3 服务启动、开机自启与默认Shell三件事

装完只是文件到位,服务还得手动拉起来,并且设置成开机自启,否则重启一次就失效:

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

第三行用来确认状态是Running。这一步做完,理论上已经可以从别的机器ssh连过来了,但连接进去后大概率会看到一个黑漆漆的cmd窗口,因为Windows版sshd的默认Shell是cmd.exe。想换成PowerShell,改注册表:

New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell ` -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" ` -PropertyType String -Force

改完不需要重启服务,下次新建连接就会生效。为什么要费这一步?因为cmd对管道、变量展开、脚本执行的支持都很弱,你ssh过去本来就是要跑脚本或做批量操作的,用cmd会处处受限。这个细节很多教程都不提,但实际用起来差别很大。

4. 离线安装:把Win32-OpenSSH手工搬进目标机器

4.1 取包这件事要走官方渠道

完全断网的机器没法用可选功能。这时需要从微软官方的PowerShell/Win32-OpenSSH项目页面下载对应版本的zip包,比如OpenSSH-Win64.zip。下载时注意区分x64和x86,虽然现在x86的机器很少,但老设备上确实存在。拿到包之后,建议在有网络的机器上先解压检查一下目录结构,确认里面有sshd.exe、ssh.exe、install-sshd.ps1这几个关键文件,再拷进内网。

拷进去的方式可以用U盘,也可以走堡垒机的中转目录。我个人的做法是先把包放到C:\Users\Public\Downloads这类公共路径,解压验证完再挪到最终位置,避免解压一半发现包损坏又要重新拷贝。

4.2 定位解压与注册服务

最终目录我一般放在C:\Program Files\OpenSSH,路径里不要有空格和中文,否则某些脚本调用会出现诡异问题。解压完成后,进入该目录,用管理员权限的PowerShell执行:

powershell.exe -ExecutionPolicy Bypass -File .\install-sshd.ps1

这个脚本会做几件事:注册sshd服务、生成主机密钥、调整文件权限、设置服务为手动启动。执行完再重复上一节的服务启动和自启设置,然后把C:\Program Files\OpenSSH加进系统PATH,这样在任何目录下都能直接调用ssh/scp。

$env:Path += ";C:\Program Files\OpenSSH" [Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine")

4.3 权限、路径与两个容易翻车的点

离线安装最常见的两个问题,第一个是脚本执行被策略拦下。如果你看到"无法加载文件,因为在此系统上禁止运行脚本",说明ExecutionPolicy是Restricted,上面那条命令里的-ExecutionPolicy Bypass就是专门绕开这个限制的。第二个是主机密钥生成失败,通常是因为解压目录的权限不对,或者解压时用了普通用户身份,导致后续服务账户没权限读密钥文件。

提示:离线安装的版本不会随系统更新自动升级,后面出现安全公告时需要你手动替换文件,建议把这个目录和版本号记进资产台账。

还有一点值得说:手动部署的版本,配置文件的默认路径依然是C:\ProgramData\ssh,而不是安装目录。很多人改错了地方的sshd_config,改完发现不生效,就是踩了这个坑。

5. sshd_config里真正需要动的几行

5.1 默认值逐条过一遍,别整份照抄别人的

C:\ProgramData\ssh\sshd_config是服务端的主配置。网上有很多"优化模板",动辄几十行改动,我实测下来大部分是没必要的。真正值得关注的就这么几项:

配置项默认值建议原因
Port22按需改端口冲突时改,改了要同步防火墙
PasswordAuthenticationyes视情况只走密钥就关掉
PubkeyAuthenticationyes保持免密登录的基础
PermitRootLogin无不适用Windows没有root概念,可忽略
Subsystem sftp已配置保持scp/sftp依赖它

改完配置一定要用Restart-Service sshd重启服务,光reload在某些版本上不生效。改之前先备份一份原文件,出问题能快速回滚。

5.2 主机密钥生成与更换

主机密钥的默认位置在C:\ProgramData\ssh\,包括ssh_host_rsa_key、ssh_host_ed25519_key等。首次安装脚本会自动生成,不需要你操心。但如果你把机器做了克隆(虚拟机模板那种),两台机器的主机密钥会完全一样,客户端连接时就会报"远程主机标识已更改"的警告。这时候需要删掉旧密钥重新生成:

Remove-Item C:\ProgramData\ssh\ssh_host_* ssh-keygen -t rsa -f C:\ProgramData\ssh\ssh_host_rsa_key -N "" ssh-keygen -t ed25519 -f C:\ProgramData\ssh\ssh_host_ed25519_key -N "" Restart-Service sshd

客户端那边的known_hosts也要清理对应条目,否则一直报警告。这个坑在批量部署虚拟机模板时几乎必踩,提前规避能省不少事。

5.3 防火墙放行与端口连通性验证

服务起来了但连不上,十有八九是防火墙。较新版本的安装流程会自动创建一条入站规则,名字一般叫OpenSSH-Server-In-TCP。用这条命令确认:

Get-NetFirewallRule -Name *ssh*

如果查不到,手动补一条:

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

验证连通性别只靠直觉,按顺序来:先在服务器本机Test-NetConnection -ComputerName localhost -Port 22,确认服务在监听;再从客户端Test-NetConnection -ComputerName <服务器IP> -Port 22,这一步能区分是网络不通还是服务没起。这两步分开做,能省掉大量瞎猜的时间。

6. 客户端那边的功夫:密钥、config与免密登录

6.1 密钥对生成与公钥落到哪里

免密登录的核心是公钥放置位置正确。先生成密钥对:

ssh-keygen -t ed25519 -C "win-server-01"

一路回车即可,私钥默认在%USERPROFILE%\.ssh\id_ed25519。然后把公钥内容追加到服务端的授权文件里。这里有个Windows特有的坑:普通用户和管理员用户用的授权文件不是同一个。普通用户放在自己的%USERPROFILE%\.ssh\authorized_keys,而管理员组成员统一读C:\ProgramData\ssh\administrators_authorized_keys。这个规则写在sshd_config末尾的Match块里:

Match Group administrators AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

很多人配了半天免密不生效,就是因为把公钥放进了个人目录,但登录的账户属于管理员组,sshd根本不看那个文件。这个细节值得单独记一笔。

6.2 用config文件把连接参数固化下来

每次敲完整命令太累,在客户端%USERPROFILE%\.ssh\config里配置别名:

Host win01 HostName 192.168.1.50 User opsadmin Port 22 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60 ServerAliveCountMax 3

配完之后ssh win01就能直接连。ServerAliveInterval这两个参数是专门对付"闲置一会儿就被断开"的问题,很多网络设备会清理长时间无流量的会话,加了这个会定时发心跳保活。

6.3 权限不对导致密钥被忽略的排查

Windows版的OpenSSH对administrators_authorized_keys的ACL要求很严格:只允许Administrators组和SYSTEM账户访问,多了任何其他账户或继承权限,sshd就会直接忽略这个文件,且日志里给的信息很模糊。修正命令:

icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

执行完可以再用icacls看一下结果,确认没有多余条目。这个问题的隐蔽之处在于:文件内容明明是对的,客户端却一直提示要输密码,而服务端日志只写一句认证失败,不告诉你原因是权限。我第一次遇到时查了快一个小时。

7. 排障实录:连不上、连上被踢、版本对不上

7.1 连接被拒的三层排查顺序

遇到Connection refused或Connection timed out,按这个顺序走,别跳步:

  1. 服务层:Get-Service sshd看服务在不在跑。不在跑就Start-Service,起不来就去看事件日志。
  2. 监听层:netstat -ano | findstr :22看端口有没有被监听。如果被别的进程占了,要么改Port,要么停掉那个进程。
  3. 网络层:从客户端Test-NetConnection测端口。不通就是防火墙或安全组的问题。

三层里最容易忽略的是第二层。有一次排查半天,最后发现是另一款软件占了22端口,sshd启动时直接失败退出,但服务状态显示得很含糊。

7.2 日志到底该看哪里

Windows版sshd的日志默认走Windows事件日志,位置在"事件查看器 → 应用程序和服务日志 → OpenSSH → Operational"。连不上、认证失败的具体原因基本都能在这里找到。如果觉得事件查看器点来点去麻烦,也可以用PowerShell直接过滤:

Get-WinEvent -LogName "OpenSSH/Operational" -MaxEvents 50 | Where-Object { $_.LevelDisplayName -in 'Error','Warning' } | Format-List TimeCreated, Message

调高日志详细程度可以在sshd_config里加LogLevel VERBOSE,调试完记得改回去,不然日志量会涨得很快。

7.3 升级之后配置不兼容怎么办

OpenSSH的版本迭代比较快,新版会对某些老配置项告警甚至拒绝启动。比如某些加密算法被标记为弱算法后,老配置里还写着就会报错。处理思路是:先看服务启动失败的报错里点名了哪个配置项,再去对应版本的发行说明里查这个项的状态。常见做法是注释掉过时项,交给默认值处理,而不是强行保留。

注意:升级前务必备份C:\ProgramData\ssh整个目录,包括配置和主机密钥。有些升级流程会覆盖配置,回滚时没有备份会很麻烦。

另外,客户端和服务端版本不需要完全一致,但跨大版本太多时可能出现算法协商失败,表现为连接建立到一半就断开,日志里能看到"no matching key exchange method found"之类的提示。这时候要么升级客户端,要么在服务端显式允许对应算法,两种方案各有权衡,生产环境优先选前者。

8. 把这套东西当长期资产来维护的一点个人习惯

配置好之后我建议做三件小事。第一,把C:\ProgramData\ssh目录和C:\Program Files\OpenSSH的版本号记进台账,下次出现安全公告时能快速定位哪些机器需要处理。第二,主机密钥生成后导出一份离线保存,机房重装系统时不至于让所有客户端的known_hosts全部失效。第三,对只走密钥登录的机器,在sshd_config里把PasswordAuthentication关掉,减少被暴力尝试的口子,同时配合登录失败次数限制。

我在实际使用中发现,Windows上的OpenSSH真正麻烦的从来不是"装"这个动作本身,而是装完之后那一堆权限、路径、组匹配的细节。把上面这些点过一遍,后续的维护成本会低很多。至于升级,我的习惯是先在测试机上跑一轮,确认现有连接方式都能用,再推到生产,毕竟这玩意儿一旦挂掉,远程运维的通道就断了,代价不小。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询