资讯动态

Windows Server 2016 安装 OpenSSH 并配置密钥登录

发布时间:2026/9/29 13:58:00 来源:尧图企业网站定制
1. 把 SSH 装到 Windows Server 2016 上到底图什么第一次有人问我「Windows Server 2016 能不能装 OpenSSH Server」的时候我的反应是能装但别指望像 Server 2019 那样一条命令搞定。Server 2016 这个版本比较尴尬它既没有把 OpenSSH 做成系统可选功能那个能力是 Server 2019 和 Windows 10 1809 之后才有的又经常被塞在机房角落跑一些不能停的老业务。所以在这台机器上装 OpenSSH Server、并把它调成密钥登录本质上是给一台老服务器补上一套现代化的远程运维通道让脚本、CI 任务、自动化运维工具能像连 Linux 一样连上它。这套东西能解决什么问题最直接的就是把远程操作从图形界面里解放出来。以前改个配置文件要开远程桌面网络抖一下画面就卡成幻灯片传个几十兆的日志要等半天。换成 SSH 之后scp拉个日志、ssh跑条 PowerShell、本地写个脚本批量推配置全都是命令行里几秒钟的事而且带宽占用极低弱网环境下体验完全是两个世界。再往上一层密钥登录解决的是密码这个最脆弱的环节机器暴露在运维网段甚至公网跳板场景下时密码登录每天被撞库几十万次是常态而一对密钥能把这些噪音直接挡在门外。这篇文章适合谁看如果你手上正好有一台 Windows Server 2016 的物理机或者虚拟机需要给它开一个稳定的命令行远程入口同时又不放心只用密码那这篇基本可以照着抄。虚拟机跑在 VMware 上的、用 SecureCRT 或终端工具做日常连接的、要给自己笔记本和服务器之间配免密通道的都能在里面找到对应的段落。我会把安装、服务注册、防火墙、密钥落位、权限修复、报错排查这几块按实际操作顺序铺开讲中间那些文档里不写但你一定会撞上的坑我也会标出来。2. 开工之前先想清楚路线再动手2.1 三条可选路线的真实差别在 Server 2016 上搞 SSH市面上的说法很乱有人说打开功能就行有人说必须下 GitHub 的包。区别在于你面对的系统版本不同。我先把三条路线摆出来你可以直接对号入座路线适用系统操作方式我的评价系统可选功能Windows Server 2019 / Windows 10 1809Add-WindowsCapability最省事但 2016 上根本不存在这个功能项手动部署 Win32-OpenSSHWindows Server 2016 及更早下载官方发布包、解压、跑安装脚本Server 2016 上的唯一正解第三方 SSH 服务端任意装商业或开源套件能跑但组件多、升级链复杂运维成本高Server 2016 的用户最容易踩的坑就是照着 2019 的教程去敲Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0然后得到一个找不到此功能的报错接着开始怀疑自己是不是敲错了。不是你敲错了是这个功能在 2016 的内核镜像里压根没打包进去。所以路线二才是我们要走的路。至于第三方套件我不是说它们不好只是在一台本来就承担业务的老机器上多装一层常驻服务、多一套独立的升级逻辑、多一份需要单独审计的配置文件出问题的时候排查成本是叠加的。Win32-OpenSSH 是微软自己维护并往上游 OpenSSH 回馈的分支配置文件格式、密钥格式、命令行参数都和 Linux 上的 OpenSSH 保持高度一致学一次能通用这才是我推荐它的核心理由。注意Server 2016 上不要尝试用启用 Windows 功能面板去勾 OpenSSH那个列表里没有浪费时间。2.2 部署前必须确认的四件事动手之前花五分钟确认环境能省掉后面半小时的抓瞎。第一件事是确认系统版本和架构打开 PowerShell 敲[System.Environment]::OSVersion.Version $env:PROCESSOR_ARCHITECTUREServer 2016 的内核版本是 10.0.14393看到这个数字就说明确实是 2016 而不是 2012 R2 混进来了。架构上现在绝大多数是 AMD64下载包要选 Win64 那一份如果你在给一台老旧的 32 位机器收尾那就得选 Win32不过这种场景现在很少见了。第二件事是确认 PowerShell 的执行策略。安装脚本是.ps1文件默认策略可能是 Restricted直接跑会被拦下来。先看一眼Get-ExecutionPolicy -List如果 CurrentUser 或 LocalMachine 显示 Restricted不用急着全局改成 Unrestricted那会降低整台机器的脚本安全基线用单次绕过的方式执行就够了后面我会给出具体命令。第三件事是确认 22 端口没有被占用。有些机器上装了别的管理软件悄悄占着 22你装完发现服务起不来还以为是 OpenSSH 的问题。Get-NetTCPConnection -LocalPort 22 -ErrorAction SilentlyContinue netstat -ano | findstr :22第四件事是拿到管理员权限。Win32-OpenSSH 的安装脚本要写服务、写C:\ProgramData\ssh、改 ACL权限不够会在中间某一步悄无声息地失败。我的习惯是直接开一个以管理员身份运行的 PowerShell 窗口全程在这个窗口里操作避免来回切换导致上下文不一致。3. 安装 Win32-OpenSSH从解压到服务跑起来3.1 版本选择与安装包落地版本这块有个取舍。新版本8.x、9.x性能和算法支持更好但对系统运行库的要求也更高老版本7.x兼容性好但在和现代客户端握手时可能出现算法协商的摩擦。我的建议是如果这台 Server 2016 装了比较新的补丁累积更新到 2019 年之后的水平优先用较新的稳定版如果是一台常年不打补丁、只跑内部脚本的机器7.7 到 7.9 这一档反而省心因为它对老客户端的 rsa 算法足够宽容。下载下来的通常是OpenSSH-Win64.zip。我习惯把它先放到C:\temp解压到C:\Program Files下面并且把解压出来的目录名从OpenSSH-Win64改成OpenSSH这样路径短、看着也整齐后续写服务路径的时候不容易出错。# 确认压缩包存在 Test-Path C:\temp\OpenSSH-Win64.zip # 解压到 Program Files Expand-Archive -Path C:\temp\OpenSSH-Win64.zip -DestinationPath C:\Program Files\ -Force # 统一目录名 Rename-Item -Path C:\Program Files\OpenSSH-Win64 -NewName OpenSSH # 看一眼关键文件在不在 Get-ChildItem C:\Program Files\OpenSSH | Select-Object Name解压完你应该能看到这几个核心文件sshd.exe服务端主程序、ssh.exe客户端、ssh-keygen.exe密钥生成工具、install-sshd.ps1安装脚本、FixHostFilePermissions.ps1和FixUserFilePermissions.ps1权限修复脚本。这几个名字记一下后面排查问题全靠它们。如果解压后双击sshd.exe直接报缺少某个 DLL多半是缺 VC 运行库。这不是 OpenSSH 的锅是它的依赖链。装上对应版本的 Microsoft Visual C Redistributable2015-2022 那一档再试一次就好这种问题在刚做完系统精简的机器上出现的概率不低。3.2 执行安装脚本并注册系统服务这一步是整个流程的分水岭。很多人解压完就手动去sc create建服务结果服务参数不对启动就失败。正确做法是跑官方自带的安装脚本cd C:\Program Files\OpenSSH # 单次绕过执行策略避免全局放开 powershell.exe -ExecutionPolicy Bypass -File .\install-sshd.ps1这个脚本干了三件重要的事注册sshd服务这是接受连接的守护进程、注册ssh-agent服务用于密钥代理可选但建议留着、以及初始化C:\ProgramData\ssh目录下的主机密钥。执行完看到类似sshd and ssh-agent services successfully installed的输出就说明注册成功。接着把服务设成自启动并拉起来Set-Service -Name sshd -StartupType Automatic Start-Service sshd # 确认状态 Get-Service sshd, ssh-agent | Format-Table Name, Status, StartTypessh-agent服务如果只是做密钥登录其实不是必须的但如果你打算在服务器上跑脚本、用 agent 转发就把它也设成 Automatic。我一般两个都开反正内存占用可以忽略。这里有个容易被忽略的细节主机密钥文件是否真的生成了。它们的位置在C:\ProgramData\ssh\下面文件名形如ssh_host_ed25519_key、ssh_host_rsa_key。如果这个目录是空的服务虽然能启动但客户端第一次连接时会因为拿不到主机指纹而报错。检查一下Get-ChildItem C:\ProgramData\ssh\ssh_host_*没有的话可以手动触发一次生成或者直接重跑一遍install-sshd.ps1。3.3 防火墙放行与第一轮连通性验证服务起来了不代表能连上Windows 防火墙默认不放行入站 22 端口。放行命令我习惯带一个明确的规则名方便以后要禁掉的时候一条命令搞定New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName OpenSSH Server (sshd) -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22放行之后在服务器本机先自测这一步能把服务问题和网络问题分开Get-NetTCPConnection -LocalPort 22 -State Listen ssh -V ssh localhost -p 22看到LISTENING状态和ssh版本号输出说明服务这半边是好的。如果本机能连、从别的机器连不上问题就在防火墙、安全组或者网络策略上别再回头折腾 sshd 配置了那是白费功夫。提示如果 22 端口已经被业务占用可以改C:\ProgramData\ssh\sshd_config里的Port参数但记得同步修改防火墙规则和客户端的连接参数两边不一致是新手最常犯的错误。4. 密钥登录把密码这道门换成一把锁4.1 生成密钥对客户端这边动手密钥登录的起点在客户端而不是服务器。你在自己常用的那台机器上生成一对密钥私钥留在本地公钥丢到服务器上。生成命令# 推荐优先用 ed25519短、快、安全强度好 ssh-keygen -t ed25519 -C opswindows2016 -f ~/.ssh/id_ed25519_win2016 # 兼容性优先时选择 RSA 4096 ssh-keygen -t rsa -b 4096 -C opswindows2016 -f ~/.ssh/id_rsa_win2016加-C参数只是给这把密钥贴个标签方便以后打开公钥文件时知道它是给哪台机器用的实际操作中非常有用尤其是手里有十几台服务器的时候。加-f是显式指定文件名避免覆盖你已有的默认密钥这一点也很重要——很多人习惯直接回车生成id_rsa结果把之前配好的覆盖掉了然后所有免密登录全部失效排查起来非常难受。私钥文件一定要设口令passphrase哪怕只是几个字符。没有口令的私钥一旦被复制走等于把服务器的钥匙直接送给对方。如果确实追求自动化场景下的免交互那就用 agent 来托管而不是把私钥裸奔。算法选择上我的实战经验是新版本服务端优先 ed25519跨平台跨工具链混合环境则 RSA 4096 更保险。原因是 OpenSSH 8.8 之后客户端默认不再接受基于 SHA-1 的ssh-rsa签名而 Server 2016 上装的如果是老版本 Win32-OpenSSH它只认ssh-rsa。这时候你会遇到一个非常迷惑的现象公钥明明放对了密码也关了客户端一直提示Permission denied (publickey)。解决办法是在客户端侧加参数放行ssh -o PubkeyAcceptedKeyTypesssh-rsa -o HostKeyAlgorithmsssh-rsa user192.168.1.10或者在客户端的~/.ssh/config里给这台主机单独写一段配置把这个兼容性选项固化下来比每次敲一长串参数舒服得多。4.2 公钥落位普通用户和管理员是两套路这是整个配置里最关键、也是最容易被文档一笔带过的地方。Windows 版 Win32-OpenSSH 的默认配置文件C:\ProgramData\ssh\sshd_config末尾有这么一段Match Group administrators AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys翻译成大白话就是如果登录账号属于 administrators 组那它读的不是用户目录下的authorized_keys而是C:\ProgramData\ssh\administrators_authorized_keys这个统一文件。很多人在服务器上给自己建的账号就是管理员然后老老实实把公钥写进C:\Users\xxx\.ssh\authorized_keys怎么连都连不上翻遍日志也看不出所以然最后怀疑是密钥有问题——其实密钥一点问题没有是放错了文件。所以落位规则是登录账号类型公钥应该写到哪个文件普通用户非管理员组C:\Users\用户名\.ssh\authorized_keys管理员组成员C:\ProgramData\ssh\administrators_authorized_keys管理员那条路径还有个额外的坑如果你只是用记事本另存为Windows 会给文件带上继承来的 ACLOpenSSH 检测到其他人可写就直接拒绝使用这个文件而且它不会在客户端报详细原因只给你一个冷冰冰的Permission denied。正确做法是写完之后显式设权限只留 SYSTEM 和 Administrators 读# 追加公钥到管理员专用文件 $key Get-Content C:\temp\id_ed25519_win2016.pub Add-Content -Path C:\ProgramData\ssh\administrators_authorized_keys -Value $key -Encoding utf8 # 收紧 ACL icacls C:\ProgramData\ssh\administrators_authorized_keys /inheritance:r /grant SYSTEM:F /grant BUILTIN\Administrators:F普通用户那条路径同样要注意权限文件不能对其他普通用户可写否则一样被拒。顺手的做法是icacls C:\Users\testuser\.ssh\authorized_keys /inheritance:r /grant testuser:F /grant SYSTEM:F另外官方包里带了两个现成的修复脚本FixHostFilePermissions.ps1修主机密钥权限和FixUserFilePermissions.ps1修授权文件权限。当你不确定权限到底对不对的时候跑一下这两个脚本比手动逐条设 ACL 靠谱得多cd C:\Program Files\OpenSSH powershell.exe -ExecutionPolicy Bypass -File .\FixHostFilePermissions.ps1 -Confirm:$false powershell.exe -ExecutionPolicy Bypass -File .\FixUserFilePermissions.ps1 -Confirm:$false注意authorized_keys文件的编码必须是 UTF-8 且不带 BOM。用记事本另存为时如果选了UTF-8 带 BOM里面会多出几个不可见字节公钥解析直接失败表现就是永远认证不通过。用 PowerShell 的Add-Content -Encoding utf8或者直接ssh-copy-id之类的工具写入更稳。4.3 调整 sshd_config 并让配置生效公钥放好之后回到服务端调C:\ProgramData\ssh\sshd_config。几个必须确认的参数PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin no AuthorizedKeysFile .ssh/authorized_keys这里有个顺序问题要提醒不要一上来就把PasswordAuthentication改成 no。万一密钥那块有问题你就把自己锁在门外了只能去机房或者开控制台救场。正确节奏是先把PubkeyAuthentication打开用密码加密钥双通道测试确认密钥登录能进再关掉密码登录。这个顺序我踩过不止一次坑尤其是远程操作一台不在身边的机器时改错一个参数就是一趟差旅。改完配置重启服务生效Restart-Service sshd Get-Service sshd这里要特别注意Windows 上不能用SIGHUP那种方式让 sshd 重载配置直接重启服务是最靠谱的。如果你改完配置没重启就测试看到的现象会是配置像没生效一样会让你怀疑是不是改错了文件。默认配置文件路径是C:\ProgramData\ssh\sshd_config不是安装目录下的那份这个也要记牢。顺便再提一个实用参数默认 shell。Server 2016 上 OpenSSH 默认把登录后的 shell 设成 cmd很多人希望进去就是 PowerShell改注册表即可New-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH -Name DefaultShell -Value C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -PropertyType String -Force改完之后重新连一次登进去就直接是 PowerShell 提示符跑脚本舒服很多。4.4 用命令行和图形客户端各验证一遍验证一定要用两种工具各走一遍因为两者的行为不完全一样。命令行用-v打详细日志ssh -v -i ~/.ssh/id_ed25519_win2016 Administrator192.168.1.10日志里重点看三行Offering public key说明客户端把公钥递出去了Server accepts key说明服务端认了Authentication succeeded才是最终成功。如果卡在第一行是客户端没找到密钥文件卡在第二行是服务端权限或路径问题。图形客户端这边SecureCRT、Xshell 这类工具都支持密钥登录。以 SecureCRT 为例会话属性里找到 SSH2 分类认证方式只勾 PublicKey然后在密钥管理器里导入你的私钥文件id_ed25519_win2016注意不是.pub再回到会话里选中这把密钥。有一个细节不少人会栽某些工具只认 PuTTY 格式的.ppk这时候需要用转换工具把 OpenSSH 格式转过去。如果导入时提示格式不对十有八九就是这个原因不是密钥坏了。5. 排障实录那些文档不写的典型故障5.1 一张排查顺序表照着走就行遇到连不上别乱试按这个顺序从下往上排能少走一大半弯路现象优先检查项常见原因连接超时无任何回应防火墙规则、网络可达性、端口监听22 未放行、监听端口被改、路由不通立即被拒Connection refused服务状态、监听端口sshd 没启动、端口被占用Permission denied (publickey)公钥落位路径、文件 ACL、算法兼容管理员写到了用户目录、文件权限过宽、sha-rsa 协商失败提示要输密码但已配密钥PubkeyAuthentication、密钥路径参数为 no、客户端没用-i指定正确私钥登录成功但立刻断开默认 shell、用户配置文件shell 路径错误、profile 脚本报错首次连接提示主机指纹异常主机密钥是否变化系统重装或密钥文件被覆盖5.2 三个我实际踩过的坑第一个坑是管理员公钥文件权限。有一次给一台机器配密钥客户端日志明明白白写着Server accepts key但紧接着就是认证失败。折腾了四十分钟最后用icacls一看administrators_authorized_keys继承了父目录的权限Users组有写权限OpenSSH 因此拒绝了这个文件。手工把继承关系断掉、只留 SYSTEM 和 Administrators 之后一次就连上了。这个问题的恶心之处在于它完全静默服务端日志只给一句很模糊的提示不主动去查 ACL 根本想不到。第二个坑是新旧版本算法对不上。一台 Server 2016 上装的是较老的 Win32-OpenSSH而我本地的客户端已经升级到很新的版本结果就是死活认证不过。加了-o PubkeyAcceptedKeyTypesssh-rsa之后立刻通了。所以碰到公钥、权限、路径全都对但就是不行的情况先怀疑算法协商用ssh -vvv看客户端和服务端各自支持什么算法比盲目改配置文件高效得多。第三个坑是补丁重启之后服务没起来。运维场景里给服务器打补丁重启是家常便饭Windows Server 2016 打完累积更新重启之后很容易出现服务状态回到手动甚至停止的情况尤其是这台机器上还跑着 RDS 相关的角色时重启后的各种会话状态本来就更需要额外确认。我的做法是在配完之后做一次完整的重启验证Restart-Computer等它起来然后立刻Get-Service sshd看状态再确认一遍监听端口和自启动类型。这一步花两分钟能避免以后每次打补丁都要人工去拉服务。# 重启后自检三连 Get-Service sshd | Select-Object Name, Status, StartType Get-NetTCPConnection -LocalPort 22 -State Listen Get-WinEvent -LogName Application -MaxEvents 20 | Where-Object { $_.ProviderName -like *sshd* }5.3 日志从哪看怎么开调试Windows 上 OpenSSH 的日志默认进 Windows 事件日志的 Application 通道来源名是sshd。用上面那条Get-WinEvent命令就能捞出来。如果事件日志里信息太少可以临时把sshd_config里的LogLevel从INFO调到DEBUG3重启服务后再连一次日志会详细到每一步的密钥协商和权限判定过程。排查完记得调回INFO否则日志文件会被快速撑大。还有一个更直接的办法停掉服务用前台调试模式跑 sshd这样所有输出直接打在屏幕上实时性最好。Stop-Service sshd cd C:\Program Files\OpenSSH .\sshd.exe -d -e-d表示单次调试运行-e表示日志输出到标准错误流而不是事件日志。这个模式下你可以一边连一边看着控制台刷日志定位问题的效率比翻事件日志高一个档次。排查完CtrlC结束再把服务正常启动回来。6. 收尾加固与长期维护的几条实战建议6.1 上线前建议过的加固清单一套能长期跑下去的 SSH 通道不只是能连上就够了。下面这些是我每台机器都会做的收尾动作逐条检查一遍花不了十分钟关闭密码登录确认密钥稳定可用后把PasswordAuthentication设为 no这是收益最高的一条。禁止管理员组批量暴露如果只是给一两个运维账号用可以在sshd_config里用AllowUsers精确限定能登录的账号其余一律拒绝。改默认端口内网环境下改端口的意义有限但如果这台机器有对外暴露的风险换个高位端口能过滤掉大量自动扫描噪音唯一代价是防火墙和客户端要同步改。限制来源地址在防火墙规则里把 RemoteAddress 限定到运维网段比任何软件层配置都硬。保留 HostKey 备份把C:\ProgramData\ssh\ssh_host_*这几个文件单独备份一份。系统重装或者误删之后如果主机密钥变了所有客户端都会弹指纹变更警告严重的还会直接拒绝连接有备份就能原地恢复。私钥集中管理服务器上的私钥文件权限要收死能用 agent 托管的场景就不要把私钥落盘。6.2 后续升级和扩展的两点思路Win32-OpenSSH 在 Windows 上不是一个随系统自动更新的组件它不会跟着 Windows Update 自己升级。这意味着你装的那个版本可能一年后还是那个版本而外部的 OpenSSH 协议和算法在持续演进。我的做法是每半年检查一次当前版本和官方发布版的差距如果差的版本多了就找一次维护窗口停服务、替换安装目录里的文件、跑一遍install-sshd.ps1、重启服务再按之前那份自检清单验一遍。整个过程十分钟以内风险可控。至于扩展方向最省事的收益是给这台机器配上自动化任务把发布脚本、日志采集、备份任务都改成通过 SSH 远程触发本地用一个免密通道串起来。一旦这条路通了你会发现自己开远程桌面的次数直线下降日常运维的效率提升比想象中明显。我个人的体会是Server 2016 这种被很多人当成过时机器的服务器只要把 SSH 和密钥这一层搭好它作为一台稳定的后台执行节点依然非常好用真正的瓶颈从来不是系统版本而是有没有一条可靠的、可脚本化的入口。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑