☰
Windows 11 OpenSSH 服务端配置:自定义端口与密钥登录实战
2026/10/1 12:21:22 网站建设 项目流程

“22 端口开着、密码是 123456、防火墙关着”——这是我帮人收拾过太多次的现场。Windows 11 自带的 OpenSSH 服务端其实已经相当能打,图形界面点两下就能装上,可真正卡住人的从来不是安装,而是后面那两步:改完端口服务起不来,公钥丢进去却依然弹密码框。这篇就把 Windows 11 上装 SSH 服务端、换自定义端口、上密钥登录这一整条链路从头到尾捋一遍,重点讲清楚每一步为什么这么做、改哪个文件、踩过的坑长什么样,以及怎么在改坏之前给自己留一条后路。不管你是想在家里那台常开的台式机上开个安全通道,还是在公司内网里给几台开发机做统一的免密登录,照着走都能落地。内容偏实操,命令可以直接复制,但每一条我都会说清楚它到底干了什么。

1. 先把方案定下来:Windows 11 跑 SSH 服务端有哪几条路

1.1 三条技术路线横向对比

在 Windows 上提供 SSH 服务端,市面上能走的路基本就三条,我先把它们摆在一起,后面你就知道为什么我最后选了最“土”的那条。

方案安装方式与 Windows 账户的整合维护成本适合场景
系统内置 OpenSSH Server可选功能里一键添加直接使用本地账户和密码体系低,随系统更新绝大多数个人和内网场景
WSL2 里跑 sshd装发行版后 apt 安装账户体系独立,路径映射麻烦中,网络转发要额外配置已有 Linux 开发环境的人
第三方 SSH 服务端下载安装包需要额外配置账户映射高,来源需谨慎特殊协议需求场景

内置方案最大的价值在于“它本来就是微软维护的组件”。你不需要引入任何来路不明的二进制文件,端口、服务、防火墙规则都是标准的 Windows 组件在管,出问题的时候排查路径最短。WSL2 方案看起来优雅,但它的 sshd 跑在虚拟网络里,宿主机要访问得做端口转发,而且默认情况下 WSL 的账户和 Windows 账户不是一回事,权限问题会叠加。至于第三方工具,除非你有明确的兼容性需求,否则没必要给自己增加一个需要长期跟进的依赖。

1.2 我为什么最终选内置 OpenSSH Server

我自己的主力机是一台常年不关的 Windows 11 台式机,主要跑一些自动化任务和本地服务。我的需求很朴素:从笔记本上敲一行ssh 主机名就能进去,不输密码,能直接用 PowerShell 干活。内置方案在这三件事上都是零障碍——它把 SSH 会话的默认 Shell 直接接到cmd.exe或者 PowerShell 上,登录进去就是你熟悉的 Windows 命令行环境,路径、盘符、环境变量全都是原生的,不需要在 Linux 语义和 Windows 语义之间来回翻译。

还有一个容易被忽略的点:内置服务端的日志会写进 Windows 事件查看器里的OpenSSH/Operational通道。这意味着连接失败的时候,你能拿到非常明确的失败原因——是密钥权限不对,还是算法不匹配,还是账户不在允许列表里,全都写得清清楚楚。用第三方方案的时候,这类信息往往要靠猜。

1.3 动手前的环境确认

动手之前有三件事必须先确认,否则后面会白折腾。

第一,确认系统版本里有没有 OpenSSH 客户端。Windows 11 默认自带客户端,服务端需要手动添加。打开 PowerShell,跑一句Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*',输出里会列出OpenSSH.Client~~~~0.0.1.0和OpenSSH.Server~~~~0.0.1.0两条,后面的State字段如果是Installed就说明已经有,NotPresent就是还没装。

第二,确认你有本机管理员权限。安装可选功能、注册服务、改防火墙规则这三件事都需要提权。建议直接用管理员身份打开 PowerShell,省得中途反复弹窗。

第三,想清楚这台机器在什么网络里。如果它连的是公司内网或者家里的局域网,那端口和认证策略可以放松一点;如果它有公网可达的地址,那下面关于端口和认证的每一步都不是可选项,而是必做项。这个判断会直接影响你后面的配置尺度,别跳过。

2. 安装与启用 OpenSSH 服务端

2.1 三种安装方式,按场景挑一个

安装 OpenSSH 服务端,图形界面和命令行都能做,效果完全一样,区别只在于你要不要批量部署。

图形界面的路径是:设置 → 系统 → 可选功能 → 点击“查看功能”或“添加可选功能” → 在搜索框里输入OpenSSH→ 勾选“OpenSSH 服务器” → 安装。整个过程两三分钟,装完之后不需要重启。

命令行更适合需要重复操作的场景,一条命令搞定:

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

这里的~~~~0.0.1.0是 Windows 可选功能的版本标识写法,四个波浪号是固定格式,别手抖改成别的符号。如果执行后返回RestartNeeded: False,说明不需要重启,服务已经就位。

还有一种离线场景:目标机器完全断网,没法从系统组件源拉包。这时候需要提前在有网的机器上把对应的OpenSSH-Server-Package组件包导出,再拷贝过去用Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 -Source <路径>安装。这条路我建议只在确实有隔离要求的时候走,因为组件包的版本要和系统版本对得上,对不上会报错,排查起来比重新装系统还费时间。

注意:安装完成后先别急着改配置。用默认配置连一次,确认服务本身是通的,再动参数。这样一旦后面出问题,你能确定是新改的那一行引起的,而不是把“服务没起来”和“配置写错了”两个问题搅在一起。

2.2 服务启动、自启与默认 Shell 调整

组件装好了不等于服务在跑。默认情况下sshd服务是手动启动状态,你需要明确地把它拉起来并设为自动。

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

第一行是立刻启动,第二行是让它在每次开机时自动起来,第三行用来确认状态是Running。这里有个细节值得说:Set-Service的-StartupType参数接受的是Automatic、Manual、Disabled这几个值,写成Auto会报错,我见过不少人在这卡半天。

接下来是默认 Shell 的问题。Windows 上 OpenSSH 默认给你一个cmd.exe,这对习惯 PowerShell 的人来说很难受,管道、&&、对象化输出全都用不了。改法是在注册表里指定:

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

如果你更习惯新版 PowerShell,把路径换成对应的pwsh.exe完整路径即可。这一步不是必须的,但改完之后整个使用体验会顺畅一个档次,尤其是要从 SSH 会话里跑脚本的时候。

2.3 首次连通性验证

在改端口、上密钥之前,先做一次最朴素的验证。在服务器本机上跑:

ssh 用户名@127.0.0.1

如果服务正常,会提示你确认主机指纹(第一次连接必有),然后要求输入密码。密码就是你 Windows 账户的登录密码。能进去说明整条链路是通的,后面所有的折腾都是在这个基础上做优化。

如果在局域网另一台机器上测试,就把127.0.0.1换成服务器的内网 IP。这一步如果连不上,先查服务状态,再查防火墙里那条OpenSSH-Server-In-TCP规则是否启用,最后才怀疑网络层。顺序别反,否则容易在一堆可能性里迷路。

顺便提一句主机指纹确认的意义。第一次连接时终端会显示一串指纹并问你是否继续,这是在防中间人。在内网环境随手确认问题不大,但如果服务器有公网可达地址,正确做法是先在本机执行ssh-keyscan或者直接从服务器上读出公钥指纹,比对一致后再确认,而不是闭着眼睛敲 yes。

3. 修改默认端口:改哪里、怎么验证、坑在哪

3.1 sshd_config 里真正要动的只有几行

配置文件在C:\ProgramData\ssh\sshd_config。注意这个路径不在你的用户目录下,是全局配置。用管理员权限的编辑器打开它,找到Port这一行。

默认情况下这一行是被注释掉的(前面有个#),注释状态意味着使用默认值 22。要改端口,把这行取消注释并改成你要的号:

Port 2222

改完保存,重启服务:

Restart-Service sshd

到这里就出现第一个经典坑:服务重启失败,或者看起来重启成功了但一连就拒绝。原因通常不在配置文件本身,而在下面两处。

一是配置文件语法。sshd_config对格式挺挑剔,Port和数字之间必须是空格,行首不能有多余字符。想提前验证语法可以跑:

& "C:\Windows\System32\OpenSSH\sshd.exe" -t

这个命令会做配置语法检查,有问题会明确报出是哪一行。养成改完就-t一下的习惯,能省掉大量“服务起不来但不知道为啥”的时间。

二是权限。sshd_config这个文件必须只有管理员组和 SYSTEM 能写,如果它的权限被改乱了,sshd 会拒绝启动。这不是 Windows 特有的洁癖,而是 SSH 的一贯设计——配置文件可被普通用户改写意味着整个认证体系形同虚设。遇到权限相关的启动失败,事件日志里会有明确提示。

3.2 防火墙规则必须同步修改,这一步最容易漏

改完配置文件、服务也起来了、本机ssh 用户名@127.0.0.1 -p 2222能连上,但从另一台机器连还是超时——这种情况九成是防火墙没放行新端口。

Windows 在安装 OpenSSH 服务端的时候会自动建一条名为OpenSSH-Server-In-TCP的入站规则,只放行 22。你换了端口,这条规则就管不着了。两种处理方式:

一种是新建一条规则,保留老的:

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

另一种是直接改老规则的端口,把 22 换成 2222。我更推荐新建一条,原因是回滚方便——万一新端口有问题,把新规则禁用、配置改回 22、重启服务,一分钟就能回到可用状态,不用去回忆原来那条规则的原始参数长什么样。

注意:这两条规则不要同时长期开着。留着 22 的放行规则,等于你改端口这件事白做了,扫描器照样能从 22 摸到你的服务。

3.3 端口验证与占用排查

改端口之前,先确认你要用的号没被占。Windows 上查端口占用有两条路:

Get-NetTCPConnection -LocalPort 2222 -ErrorAction SilentlyContinue netstat -ano | findstr :2222

第一条是 PowerShell 原生的写法,输出更规整,会带上占用端口的进程 ID 和进程名。第二条是老牌的netstat,兼容性好,输出的最后一列也是 PID,拿到 PID 之后用Get-Process -Id <PID>就能知道是哪个程序。这两条命令建议都记住,因为netstat在某些精简系统上可能被移除,而Get-NetTCPConnection是 PowerShell 自带模块,只要系统不残缺就有。

验证新端口能不能通,用这个:

Test-NetConnection -ComputerName 127.0.0.1 -Port 2222

返回里TcpTestSucceeded是True就说明本地监听正常。这一步的价值在于把“服务问题”和“网络问题”分开——本地通了但远端不通,问题一定在防火墙或路由上,不用再回头怀疑 sshd。

3.4 端口号怎么选才不踩雷

选端口号这件事看着随意,其实有几个明确的雷区。

避开 1024 以下的号。这些是特权端口,虽然 Windows 的权限模型和 Linux 不完全一样,但低端口段经常被系统服务占用,冲突概率高,而且排查起来麻烦。

避开常见服务的默认端口。比如 8080、3306、6379、5000、8000 这些,你的机器上大概率跑着别的开发服务,撞上了就是两个程序抢一个号。

避开被系统和安全软件盯上的号。比如 445、135、139 这些,很多安全策略默认就对它们有额外限制或者监控,用它们做 SSH 端口会引入一堆无关变量。

我的实际选择习惯是在 20000 到 60000 之间挑一个自己好记的,比如22222、40022这类。它不需要真的“隐蔽”,改端口的核心价值是减少自动化扫描器带来的无效连接和日志噪音,而不是把服务藏起来。如果一台机器真的有公网可达的地址,光靠换端口是挡不住定向扫描的,真正的防护要靠密钥认证和账号策略。

4. 密钥登录:从生成到关闭密码认证

4.1 客户端生成密钥对与算法选择

密钥对在客户端生成,不是在服务器上生成。这一点很多人会搞反,结果把私钥留在了服务器上,等于白做。

ssh-keygen -t ed25519 -C "workstation-2024"

-t指定算法。现在首选ed25519,它的密钥短、签名快,而且不像 RSA 那样需要纠结长度。如果目标环境有老旧的 SSH 实现,可能只认 RSA,那就退而求其次:

ssh-keygen -t rsa -b 4096 -C "workstation-2024"

-b 4096是密钥长度,别用 2048,现在这个长度已经没有余量了。-C后面的内容是注释,会写进公钥文件末尾,用来标记这把钥匙的用途和设备来源。等你手上有五六把钥匙的时候,这个注释就是救命的,不然你根本分不清哪把是哪把。

执行过程中会问你保存路径和密码短语。保存路径默认在用户目录的.ssh下,回车即可。密码短语这一项,我的建议是:给个人工作站上的钥匙设,给无人值守的自动化任务用的钥匙不设。前者增加一层本地保护,后者设了就没法自动登录了。这不是洁癖,而是权衡。

生成完会有两个文件:id_ed25519是私钥,永远不要离开你的机器;id_ed25519.pub是公钥,拿去放到服务器上。

4.2 公钥部署:普通用户和管理员用户是两套路径

这是 Windows 上最容易翻车的地方,必须讲清楚。

对于普通用户(不属于管理员组),公钥放到:

C:\Users\<用户名>\.ssh\authorized_keys

对于管理员组的用户,默认配置下 sshd 不会去看用户目录下的那个文件,而是去看:

C:\ProgramData\ssh\administrators_authorized_keys

为什么这么设计?因为管理员账户可以在自己目录下随意改权限,如果把公钥的校验权交给用户自己,那就等于让用户自己决定谁能进来,这是权限提升的漏洞。所以系统强制把管理员组的公钥集中放到一个只有 SYSTEM 和管理员组能写的地方。

这个区别导致了一个非常典型的症状:你按教程把公钥写进C:\Users\你的名字\.ssh\authorized_keys,权限也调好了,结果登录还是弹密码框。原因就是你的账户在管理员组里,sshd 压根没看你那个文件。解决办法是打开sshd_config,找到文件末尾这段:

Match Group administrators AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys

确认它没有被注释掉(这是默认行为),然后把公钥内容写进C:\ProgramData\ssh\administrators_authorized_keys。注意__PROGRAMDATA__是一个占位符,会解析成实际的C:\ProgramData。

写完文件之后,文件末尾必须有换行符。这个坑相当隐蔽:如果最后一行公钥后面没有回车,某些情况下 sshd 会解析失败,表现为所有密钥都不生效。用记事本或者编辑器保存时按一下回车再保存,能躲开这个问题。

4.3 权限修复实操:icacls 命令逐条拆解

文件放对位置只是一半,权限对不上照样不认。SSH 的规则是:authorized_keys 文件不能被除所有者以外的普通用户读写,否则拒绝使用。

对普通用户的文件,执行:

icacls "C:\Users\用户名\.ssh\authorized_keys" /inheritance:r icacls "C:\Users\用户名\.ssh\authorized_keys" /grant "用户名:F" icacls "C:\Users\用户名\.ssh\authorized_keys" /grant "SYSTEM:F"

第一条/inheritance:r是移除从父目录继承来的所有权限条目。这一步是关键,因为 Windows 默认会把上级目录的权限继承下来,很容易出现一些不该有的条目。第二条把完全控制权给文件所有者,第三条给 SYSTEM。给 SYSTEM 是必要的,因为 sshd 服务是以 SYSTEM 身份运行的,它得能读到这个文件。

对管理员的集中文件,标准做法是:

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这个组而不是具体用户。这是因为文件是给整个管理员组共用的,把权限收到组一级,任何人往里面加公钥都需要管理员权限,安全边界更清晰。

注意:不要图省事用/grant Everyone:F之类的写法。这样做确实“能连上”,但它同时意味着这台机器上任何本地账户都能改这个文件、给自己加一把钥匙。省下的一分钟,要用后面所有的安全假设来还。

4.4 关闭密码登录的时机和回滚预案

公钥登录验证通过之后,下一步是关掉密码认证,这样暴力破解就彻底没有着力点了。在sshd_config里改这两行:

PasswordAuthentication no PubkeyAuthentication yes

但关之前必须有回滚预案,否则一条配置写错就把自己锁在外面,只能物理接触机器去救。我的操作顺序是这样的:

第一步,保持密码认证开着,用密钥登录成功一次,确认不需要输密码。

第二步,打开一个新的终端窗口,不关掉当前这个已经登录的会话,在新窗口里再用密钥连一次。这一步是为了确认配置对“新连接”生效,而不是复用了旧会话的缓存状态。

第三步,在确认前两步都稳的前提下,改配置、重启服务。然后用第三个新窗口测试密钥登录是否依然正常。如果正常,密码认证的关闭就算成功了。

第四步,保留当前已经打开的会话不要关,直到你从另一个终端完整走通一遍“从零建立连接”的流程。

这个流程看着啰嗦,但它把“配置错误导致无法登录”这个风险压到了最低。我见过有人改完配置直接重启服务再关掉所有窗口,然后发现连不上了,最后只能扛着键盘去机房。多留一个新窗口的成本,比跑一趟机房低太多了。

5. 多用户与多密钥管理

5.1 一个用户挂多把公钥

authorized_keys文件里可以放多行,每行一把公钥,登录时任一把对应的私钥都能通过。文件格式是每行一条,公钥的主体加注释,中间用空格分隔。

这个能力在实际使用里非常有用。比如你有台式机、笔记本、还有一台用于自动化的迷你主机,三台设备分别生成密钥对,把三把公钥都追加到目标机器的authorized_keys里。任何一台丢了或者重装了,你只需要删掉对应的那一行,不影响其他设备继续使用。相比“所有设备共用一把钥匙”,这种做法的好处是撤销粒度细,出了问题能精确定位是哪台设备。

追加的时候注意别覆盖。用Add-Content而不是Set-Content:

Add-Content -Path "C:\Users\用户名\.ssh\authorized_keys" -Value (Get-Content .\id_ed25519.pub)

5.2 多用户共存时的权限隔离

如果这台机器上有多个账户都要开 SSH,权限隔离就成了必须处理的事。核心原则是:每个用户的.ssh目录和里面的文件,只能由该用户本人、SYSTEM 和管理员组访问,用户之间不能互读。

检查现有权限用:

icacls "C:\Users\用户名\.ssh"

输出里如果出现了其他普通用户名,说明继承链有问题,需要清理。清理的时候注意只移除用户级的条目,系统级的SYSTEM、Administrators、Users这些内置主体要保留——移除Users这个组有时候会让某些依赖它的系统行为失效。

还有一个容易忽略的点:.ssh目录本身也有权限要求。如果目录允许其他用户写入,攻击者可以把自己的公钥文件塞进去,所以目录权限和文件权限要一起检查。这一点在多人共用的开发机上尤其重要。

5.3 客户端 config 文件让连接变省事

端口改了、密钥有了、用户也定了,但每次连接还要敲一长串参数实在难受。客户端的config文件可以解决这个。

在客户端的C:\Users\<用户名>\.ssh\config(Windows 下是同一个相对路径)里写:

Host devbox HostName 192.168.1.50 Port 22222 User myuser IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60

写完保存,之后只需要ssh devbox四个字母就能连上。ServerAliveInterval 60这行的作用是每 60 秒发一次保活探测,防止长时间空闲后连接被中间的设备悄悄掐断。这个参数在内网环境不明显,但如果你经常需要挂着一个会话跑长任务,它能省掉很多“莫名其妙断线”的困惑。

多台机器可以写多个Host段,互不干扰。这个文件的价值在于把所有连接参数从脑子里挪到文件里,换机器、重装系统的时候拷一份就完事,不用再去翻文档找回端口号是多少。

6. 故障排查实录与常见问题速查

6.1 日志和调试模式怎么开

排故障先看日志,Windows 上 OpenSSH 的日志在事件查看器的这个路径:

应用程序和服务日志 → OpenSSH → Operational

用命令行拉最近的记录更高效:

Get-WinEvent -LogName "OpenSSH/Operational" -MaxEvents 50 | Format-Table TimeCreated, Message -AutoSize

日志里能看到连接建立、认证尝试、认证失败的具体原因。比如密钥被拒绝的时候,会明确写出是被权限检查挡下来的还是文件不存在。

如果日志还不够清楚,可以让 sshd 以前台调试模式临时跑一下:

Stop-Service sshd & "C:\Windows\System32\OpenSSH\sshd.exe" -d -p 22222

-d是调试模式,会把握手的每一个细节打到终端上。这个模式下它只处理一个连接,调试完按 Ctrl+C 结束,然后记得把服务重新启动。这招在排查“公钥明明放对了却还是失败”这类问题时特别管用,因为你能直接看到它到底去找了哪个文件。

6.2 常见问题速查表

下面这些是我和身边人实际撞过的,按症状整理成表,方便对照。

症状最可能的原因处理方向
服务起不来配置文件语法错或文件权限被改乱sshd -t检查语法,检查 sshd_config 的权限
本机能连远端连不上防火墙没放行新端口新建或修改入站规则,放行对应 TCP 端口
改了端口还是从 22 能连老的防火墙规则没关禁用原有的 22 放行规则
一直弹密码框,密钥不生效管理员账户的公钥放错位置改放到 administrators_authorized_keys
密钥文件正确但认证失败authorized_keys 权限过宽用 icacls 移除继承并只保留本人和 SYSTEM
密钥内容正确但解析失败文件末尾缺少换行在最后一行后补一个回车
连接后立刻断开默认 Shell 路径写错检查注册表 DefaultShell 的路径是否存在
连接一段时间后自动断空闲超时客户端加 ServerAliveInterval,服务端检查 ClientAliveInterval
端口冲突无法启动目标端口被别的进程占用Get-NetTCPConnection查占用进程并换号
提示主机指纹变化服务器重装或密钥重新生成确认是本人操作后再清理 known_hosts 对应条目

这张表里最容易反复出现的是第二行和第四行。前者是配置和网络两个层面的问题叠加,后者是 Windows 特有的路径规则。把这两条吃透,剩下的大部分问题都能自己解决。

6.3 几条长期维护上的建议

第一,给配置改动留个记录。sshd_config里改了哪些行、为什么改、什么时候改的,写在文件末尾的注释里。半年之后你或者接手的人回来看,能立刻明白当前的配置是什么状态,不用去猜哪一行是默认值、哪一行是被人改过的。

第二,定期清点密钥。authorized_keys里积累的密钥会随着时间越来越多,有些对应的设备早就报废了。每隔几个月看一遍,把注释里写着已经不存在设备的行删掉。这个动作花不了几分钟,但它能实实在在缩小暴露面。

第三,注意系统更新后的行为变化。OpenSSH 组件跟着系统更新走,偶尔会因为安全策略调整而收紧默认值。更新之后如果 SSH 突然连不上,先别怀疑自己的配置,去看看是不是新版本对某个算法或者某个默认项做了变更。

第四,主机密钥的变化要有意识。如果某天连接时突然提示指纹不匹配,先停下来确认原因——是服务器重装了,还是有人在中间做了什么。确认是前者之后再清理客户端的known_hosts条目。这个确认动作三秒钟,但它对应的场景很重要。

我自己在这个配置上前后折腾了好几轮,最早的版本是纯密码加默认端口,后来一步步加上自定义端口、管理员集中公钥、关闭密码认证。中间最耗时间的不是配置本身,而是第一次遇到“管理员账户的公钥路径和普通用户不一样”这件事,当时对着日志翻了很久才看明白。后来再看这件事,其实只要在动手之前把 Windows 对管理员组的这块特殊处理搞清楚,整个流程十几分钟就能走完。所以如果你正准备做这件事,建议先把第 4 章那两条路径的区别记牢,能省掉大半个下午。

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

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

立即咨询