前阵子有个同事跑来问我:Windows服务器能不能用SSH登录?他手头一台Windows Server 2019,平时要么远程桌面连上去,要么直接在机房控制台里操作,想跑个脚本还得开图形界面,难受得很。我反问他:你装OpenSSH Server了吗?他愣了几秒,说这玩意儿不是Linux专属吗?Windows上还能搞?
实际上,从Windows 10 1809和Windows Server 2019开始,OpenSSH就已经作为系统可选功能被内置了。也就是说,现在的Windows服务器完全可以原生支持SSH登录,不需要装额外的第三方服务端。我把从安装、密钥配置、端口转发到故障排查的完整过程整理在这里,几十台Windows服务器实测下来,足够稳定,也希望给有同样疑问的人一个靠谱的参考。
1. 先下个结论:Windows早就可以原生跑SSH了
1.1 SSH从来不是Linux的专利
SSH(Secure Shell)本质上是一个加密网络协议,用来在不安全的网络上安全地运行网络服务,最常见的用途就是远程登录和远程命令执行。它不挑操作系统,Linux可以用,Unix可以用,Windows同样可以用。
早年Windows服务器确实没有官方的SSH服务端,大家想实现远程命令行操作,普遍会用远程桌面(RDP),或者装一些第三方SSH服务端软件。但远程桌面是图形界面,自动化脚本能力差,而且容易受会话限制;第三方软件则要关注维护和兼容性,总归不太省心。
从Windows 10 1809、Windows Server 2019这一代开始,微软把OpenSSH客户端和服务端都做成了系统可选功能。Windows 11以及更新的Windows Server版本也一直内置维护着这套组件。所以现在的Windows服务器使用SSH登录,是系统级支持的“正路”,不是歪门邪道。
1.2 原生OpenSSH与第三方工具怎么选
我见过不少运维还在推荐老牌的FreeSSHd、Bitvise SSH Server,这些工具本身也有自己的应用场景。但如果你问我现在新装一台Windows服务器到底用哪个,我的建议非常明确:优先用系统自带的OpenSSH Server,除非你有非常特殊的需求。
两者对比下来,原生方案有几个很实在的优势:
| 对比维度 | Windows自带OpenSSH Server | 第三方SSH服务端 |
|---|---|---|
| 安装维护 | 系统内置组件,更新随系统更新,无额外依赖 | 需要单独安装,版本更新靠自己盯 |
| 权限体系 | 直接映射Windows本地用户、用户组、ACL权限 | 部分工具需要单独维护虚拟账号或映射关系 |
| 自动化支持 | 可通过PowerShell、GPO统一配置和下发 | 多为独立配置,批量管理比较麻烦 |
| 安全性 | 持续安全维护,社区样本多,问题曝光快 | 小圈子工具经常无人维护,存在潜在风险 |
| 生态兼容 | 完美支持现代SSH客户端、scp、sftp、密钥认证 | 部分老牌工具对现代密钥算法支持不积极 |
当然,我并不是说第三方工具一无是处。比如某些工具自己实现了虚拟用户、虚拟目录、基于Web的管理后台,确实有适合它的人。但对于大多数希望“服务器就用标准协议去管理”的工程师,原生OpenSSH是更省心、更安全的选择。
1.3 先确认系统和现有组件
在动手之前,先花1分钟确认两件事。
第一,系统版本是否支持。Windows Server 2019、Windows Server 2022、Windows 10 1809以上、Windows 11都是没问题的。老系统比如Windows Server 2012 R2,原生装不了,但可以通过手动安装Win32-OpenSSH来凑合,不过不推荐,兼容性和维护成本都比较高。
第二,确认客户端和服务端的现状。在Windows的PowerShell窗口里执行:
powershell复制ssh -V
如果输出类似OpenSSH_for_Windows_9.5p1的版本号,说明SSH客户端已经可用了。如果提示找不到命令,说明系统里连客户端都没装,那更别说服务端。
检查服务端是否已安装:
powershell复制Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
这条命令会列出OpenSSH.Client和OpenSSH.Server两个可选功能的安装状态。如果State显示Installed,说明已经装好了,不用重复装;如果显示NotPresent,那接下来就按下面的方法装。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 安装OpenSSH Server:图形界面和命令行两条路都要会
2.1 图形界面安装五步走
如果你的服务器允许用图形界面的服务器管理器,那安装OpenSSH Server很简单。
依次打开“设置” -> “系统” -> “可选功能”,点击“添加可选功能”或“添加功能”。在列表里找到“OpenSSH 服务器”,勾选后点击安装。安装完成后,在“服务”窗口里找到名字为OpenSSH SSH Server(服务名是sshd)的服务,启动并设置启动类型为“自动”。
图形界面适合偶尔装个一两台的情况,操作直观,不用记命令。但它有个缺点:在Server Core或者没有图形界面的环境下完全无法使用。所以还是要掌握命令行安装法。
2.2 命令行安装:一条命令装好,两条命令跑起来
在Windows PowerShell(管理员)里依次执行:
powershell复制Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
执行完成后,系统会自动把OpenSSH Server相关文件放到C:\Windows\System32\OpenSSH目录下,并把sshd服务注册好。接下来启动服务并设置开机自启:
powershell复制Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
这里多说一句,不少朋友安装完成后会踩一个坑:忘了把sshd服务设为自动启动。重启服务器后,服务是停着的,SSH连接直接失败,还会误以为是安装出了问题。所以说,不要跳过Set-Service这一条,它就是负责把服务开机自启的。
如果只是想临时用一下,不设自启也可以。但生产环境强烈建议还是设成Automatic,否则哪天重启后你就只能远程桌面进去手动拉服务了。
2.3 防火墙放行22端口有两种姿势
大多数情况下,安装完成后系统会帮你在防火墙里自动创建一个名为OpenSSH SSH Server (sshd)的入站规则,允许TCP 22端口进入。但以防万一,还是手动确认一下:
powershell复制Get-NetFirewallRule -Name *ssh* | Format-List
如果规则状态正常且有Action Allow,那就不用额外操作。如果规则缺失,或者你希望把端口改成自定义端口,那就需要手动创建防火墙规则。下面这条命令把TCP 22端口重新放行:
powershell复制New-NetFirewallRule -Name 'sshd' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
我个人的建议是:对于生产环境,尽量不要把22端口直接暴露到公网。如果一定要从外网访问,那就用密钥认证,并且考虑只放行特定来源IP,或者结合安全组策略做限制。这个后文讲安全基线时再展开。
3. 密钥登录配置:管理员和普通用户的公钥文件位置天差地别
3.1 为什么强烈建议用密钥而不是密码
服务器管理里,密码登录最大的问题是:密码会被暴力破解尝试,哪怕你设置得再复杂,只要暴露到公网,每晚都能看到一堆登录失败的日志。而SSH密钥登录,用的是非对称加密,私钥不离开客户端,公钥放在服务器上。攻击者就算拿到公钥,也无法反向推导出私钥,安全性高出一个量级。
另外对于自动化场景,密钥登录可以避免在脚本里硬编码密码。配合ssh-agent使用,还能免密执行批量命令、定时备份,这些都是运维效率的硬提升。
3.2 生成密钥对并部署公钥
在任意一台客户端机器(可以是Windows、Linux、macOS)上执行:
bash复制ssh-keygen -t ed25519 -C "your_email@example.com"
这里我推荐使用ed25519算法,它的生成速度快,密钥长度短且安全性高,现代SSH客户端默认都支持。如果对方是特别老的系统,可能需要改用rsa,但以目前Windows Server 2019/2022的OpenSSH版本来说,ed25519完全没问题。
生成完毕后,默认会保存在用户主目录下的.ssh文件夹里,id_ed25519是私钥,id_ed25519.pub是公钥。
接下来要把公钥的内容放到Windows服务器上。你可以在Windows服务器的目标用户目录下创建一个文本文件,内容粘贴公钥,然后保存为authorized_keys。但注意,这里有一个大坑——管理员用户的文件路径与普通用户完全不同。
3.3 管理员账户的公钥为什么要放在那个奇怪的位置
这是Windows版OpenSSH最容易踩的坑,没有之一。
如果你登录Windows服务器的用户属于Administrators组,默认情况下sshd_config里有一个特殊的配置块:
code复制Match Group administrators
AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
它会把管理员组用户的authorized_keys指向C:\ProgramData\ssh\administrators_authorized_keys,而不是固定的C:\Users\用户名.ssh\authorized_keys。
很多朋友拿着普通Linux的习惯,把公钥放到C:\Users\Administrator.ssh\authorized_keys,然后怎么连都提示Permission denied,非常打击信心。
- 普通用户(非管理员组):公钥放在C:\Users<用户名>.ssh\authorized_keys
- 管理员组用户(比如Administrator):公钥放在C:\ProgramData\ssh\administrators_authorized_keys
管理员组的公钥文件权限也相当讲究。由于该文件位于ProgramData目录下,IIS_IUSRS等账户可能对其有读取权限,而sshd服务要求这个文件不能被非授权的账户访问,否则会拒绝使用该文件进行认证。实际操作中,我通常用下面两条icacls命令把继承权限清掉并只保留SYSTEM和Administrators组:
powershell复制icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant "SYSTEM:F" /grant "Administrators:F"
如果你使用的是普通用户,文件放对位置后,通常不需要额外处理权限。但如果出现权限级别的报错,可以同样用icacls检查用户目录下的.ssh文件夹及authorized_keys文件的权限是否过于开放。
3.4 sshd_config里需要检查的配置项
打开Windows服务器上的C:\ProgramData\ssh\sshd_config,确认下面几个关键项。
bash复制PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication yes
PubkeyAuthentication必须为yes,否则公钥认证不会生效。AuthorizedKeysFile定义了普通用户公钥的相对路径,默认是.ssh/authorized_keys,对应C:\Users<用户名>.ssh\authorized_keys。管理员组的路径已经被Match Group administrators块覆盖,这点不要去改,除非你完全清楚后果。
修改完sshd_config后,需要重启sshd服务才能生效:
powershell复制Restart-Service sshd
关于PasswordAuthentication,个人建议在密钥配置验证成功后,把它设置为no,彻底关闭密码登录。这样能极大降低暴力破解风险。但在关闭前一定要先确认密钥登录已经可用,否则你很容易把自己锁在门外。
4. 实测踩坑:连接被拒、密钥报错、登录卡顿的完整排查链路
4.1 连接被拒先分层查:服务、端口、防火墙
连接被拒绝是SSH排错里最常见的现象。遇到这类问题千万不要凭感觉乱试,我按自己的排查顺序说一下。
第一步,从客户端执行:
bash复制ssh -v user@windows_server_ip
加上-v参数后,客户端会输出很多调试信息。如果看到Connection refused,说明服务器上根本没人监听22端口,或者端口不对。先在服务器本机上确认服务状态和监听端口:
powershell复制Get-Service sshd
netstat -an | findstr :22
Get-Service查看sshd服务是否处于Running状态;netstat确认是否有进程监听在22端口(IPv4或IPv6)。如果服务没起来,参考前面Start-Service启动它;如果服务起来了但22端口没监听,多半是配置有误,或者ssh-agent这类依赖服务异常。
如果端口监听正常,但客户端仍然Connection refused,那就要看防火墙了。在本机临时关闭防火墙测试连接是否恢复,可以快速判断是不是防火墙拦了:
powershell复制Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
测完务必立刻重新打开。如果是防火墙拦的,检查那条名为OpenSSH SSH Server (sshd)的入站规则,确认作用域、端口、协议都正确。
4.2 密钥配置正确却登录失败:去事件查看器里找答案
密钥配置后仍然登录失败,最常见的两个原因:一是公钥放错位置,二是文件权限不对。但服务器并不会告诉你具体原因,它只会拒绝认证。这时候去Windows的事件查看器里翻日志,会得到更多有效线索。
打开“事件查看器” -> “应用程序和服务日志” -> “OpenSSH” -> “Operational”。sshd服务会把认证相关的详细信息写在这里,包括具体是哪个用户认证失败、认证失败的原因是什么。比如,如果看到类似“Authentication refused: bad ownership or modes for file”的日志,说明权限有问题;如果日志里写的是“No matching key exchange method”,那一般是双方密钥算法不兼容。
有一次我在客户现场排查一台Windows Server 2022,普通用户SSH密钥登录一直失败,事件查看器里反复出现“sshd: PID XXXX: error: Could not load host key: C:\ProgramData\ssh\ssh_host_ed25519_key”之类的信息。结果发现是服务器缺少主机密钥文件导致的。重新生成主机密钥并重启服务后,问题解决。命令如下:
powershell复制ssh-keygen -A
Restart-Service sshd
这类问题不多见,但如果遇到了,按上面两条命令处理即可。
4.3 登录卡在“Password:”迟迟不提示,多半是UseDNS
另一种非常常见的现象:用密码或密钥登录,客户端要等上十几秒甚至几十秒才收到服务器的响应。网络是通的,服务也正常,就是卡,非常磨人。
这通常是因为sshd_config里开启了DNS反向解析,默认配置可能为UseDNS yes。sshd服务会对发起连接的客户端IP进行反向DNS解析,如果DNS配置不好,或者客户端IP与域名对应关系混乱,解析就会超时,造成登录卡顿。
把UseDNS改为no并重启sshd服务,问题基本就能解决:
powershell复制Write-Output 'UseDNS no' | Add-Content C:\ProgramData\ssh\sshd_config
Restart-Service sshd
注意用Add-Content追加前,先确认文件里没有重复的UseDNS项,如果已经存在,手动编辑改为no即可。
4.4 密码正确却拒绝:检查PasswordAuthentication和账户策略
如果你在配置完密钥认证后关掉了密码认证,回头想用密码登录,发现提示Permission denied,这其实不是bug,而是PasswordAuthentication no生效了。
反过来,如果PasswordAuthentication是yes,密码也正确,但仍然登录失败,那就要看Windows账户本身有没有问题。比如账户是否被禁用、是否处于锁定状态、密码是否过期。Windows的默认策略是账户密码过期后无法直接SSH登录,但远程桌面会有提示,SSH这里就只会报“Permission denied”。这种情况下你要么更新密码,要么在服务器上解除账户过期状态。
我还遇到过一种小众场景:Windows系统里,某个用户的家目录路径设置有问题,导致sshd在登录时找不到有效的主目录,也会拒绝连接。可以通过PowerShell查看用户属性:
powershell复制Get-LocalUser
Get-CimInstance Win32_UserProfile | Where-Object {$_.LocalPath -like '*用户名*'}
如果UserProfile存在但状态异常,删掉重建或者处理临时配置,也能恢复。
5. SSH隧道玩法:一条命令访问Windows服务器后面的内网服务
5.1 本地转发:从办公电脑直通内网数据库管理页
Windows服务器通常不只是用来登录的,它往往和一些内网服务待在一起,比如数据库、内部管理平台、监控系统。这些服务可能并没有直接暴露公网,但你人又在办公室外,怎么方便地访问?
SSH本地转发可以帮你。假设Windows服务器(跳板机)能访问内网的某个Web管理台(IP 192.168.1.10,端口8080),你在笔记本电脑上执行:
bash复制ssh -N -L 8080:192.168.1.10:8080 user@win_server
然后在本地浏览器打开 http://127.0.0.1:8080
你会发现可以访问到原本只能在服务器所在内网里才能打开的页面。原理并不复杂:SSH客户端在本地监听了8080端口,所有发送到该端口的数据,都会通过SSH加密隧道传输到Windows服务器,再由服务器代为访问192.168.1.10:8080,最后把响应原路返回。
这个操作在管理大量内网服务时非常顺手,不用在防火墙上一个个开端口,也不用担心内网服务直接暴露到公网的安全风险。只要SSH服务本身是安全的,这层隧道就相当于替你安全地访问了内网资源。当然,你仍然需要遵守公司网络安全规范,只用它访问你有权限访问的系统。
5.2 远程转发:把Windows服务器上的流量均匀带出去
本地转发是把远处的服务“拉到本地”,远程转发则相反:把本地端口映射到服务器侧。比如你在笔记本电脑上跑了一个本地的调试服务,端口3000,想让自己在另一台机器上也能访问到它,可以这样:
bash复制ssh -N -R 3000:127.0.0.1:3000 user@win_server
这条命令在Windows服务器上监听3000端口,然后把流量全部转发到笔记本的3000端口上。这个能力在开发调试、回调测试等场景里很实用。但同时也必须强调:只要在服务器上开放了端口,就等于向外面的网络暴露了入口,一定要评估好访问控制,避免被无关人员利用。
5.3 VS Code Remote-SSH:远程编辑Windows代码的新姿势
我把VS Code和SSH放在一起提,是因为很多开发者还不知道这个用法。装好Remote-SSH扩展后,你可以直接用VS Code连接Windows服务器,然后在本地窗口里编辑服务器上的代码,就像操作本地项目一样。这个功能非常适合那些“代码在Windows服务器上,但你在工位/家里远程写代码”的场景。
配置好SSH密钥之后,VS Code会自动读取本地的~/.ssh/config,直接在远程资源管理器里选中目标服务器就能连上,比开远程桌面省心多了。注意服务器上需要提前安装好远程开发需要的工具链,比如Python、Node.js等。
在远程资源管理器里连接服务器时,如果在同步扩展和下载远程服务时卡住,可以先确认服务器能正常访问外网(扩展需要下载VS Code Server),或者提前把远程安装包传到服务器上离线安装。这一步在严格的网络隔离环境里尤其常见。
6. 把这套环境用顺手:scp传文件、远程执行命令与安全基线
6.1 scp在Windows上的正确姿势
SSH打通之后,scp传文件就成了Windows服务器和本地机器之间的“数据快车”。很多朋友用惯了共享文件夹、U盘、甚至微信传文件来在服务器和本机之间拷贝文件,其实scp既安全又直接。
在本地机器(Windows/Linux/macOS均可)上执行:
bash复制scp C:\local\report.pdf user@win_server:C:/Users/user/Desktop/
把服务器上的文件拉回本地:
bash复制scp user@win_server:C:/Users/user/Desktop/report.pdf C:\local\
其中需要注意Windows路径在scp命令里的写法。目标路径里的盘符要用C:/Users/user/Desktop/这种斜杠格式,不要写成C:\Users\user\Desktop\,否则反斜杠会被当成转义字符。这个细节如果不注意,会被各种报错折磨得怀疑人生。
如果文件多、目录大,scp效率会下降,此时可以改用sftp协议配合同步工具,或者用rsync for Windows。但日常小文件、配置文件备份,scp已经绰绰有余了。
6.2 远程执行命令:一条SSH同时跑Windows命令和PowerShell
SSH登录后可以直接跑Windows命令,也支持切换到PowerShell执行复杂脚本。比如在本地命令行里:
bash复制ssh user@win_server "powershell -Command Get-Service sshd"
或者登录后进入命令行,执行:
powershell复制powershell -ExecutionPolicy Bypass -File C:\scripts\deploy.ps1
这种方式很适合做批量运维:将多台Windows服务器的IP和账号写进脚本,通过SSH批量执行补丁更新命令、重启服务、抓取状态等。既不需要每台都开WinRM,也能享受SSH加密通道的便利。
6.3 让SSH更安全:关闭密码、限制端口、定期清理密钥
密钥认证配置完成后,我强烈建议把密码登录关掉:
powershell复制# 手动把C:\ProgramData\ssh\sshd_config中的PasswordAuthentication改为no
Restart-Service sshd
下一步是限制端口。如果在生产环境,建议不要直接把22端口暴露到公网,至少在云安全组和Windows防火墙两层都做来源IP限制。也可以在sshd_config里修改Port字段,将SSH服务放到自定义端口,配合防火墙规则放行。虽然这个做法不能完全规避扫描,但能降低脚本自动扫描的命中率。我一般是把自定义端口写在防火墙放行规则白名单里,再配合IP限制。
最后,还要定期清理authorized_keys和administrators_authorized_keys。每次有同事离职、项目外包到期、开发机替换,都可能留下不该继续存在的公钥。哪怕密钥泄露的概率很低,定期检查仍然能让安全基线保持在一个可控的水平。
6.4 用SSH统一Windows和Linux服务器的管理入口
过去很多运维团队把Windows和Linux当作两套完全割裂的管理体系:Linux用SSH,Windows用远程桌面,操作习惯、自动化工具、审计方式都不一样。
现在有了原生SSH,Windows服务器完全可以纳入统一的管理矩阵。你可以在同一个终端窗口里,用同一个SSH客户端配置、同一个密钥体系、同一套自动化管道,同时管理Linux的nginx和Windows的IIS。这对日常效率提升非常明显。
有了统一入口之后,Windows服务器从被“图形界面绑定”的旧形象里彻底走了出来,它和Linux服务器一样,可以被脚本化、可编程地管理。这也是我写这篇文章的最原始动力:别再把Windows当成只能点鼠标的服务器了,SSH能做的,它都能做,而且做得相当标准。
