1. 这不是“Linux式SSH”的简单移植,而是Windows原生远程控制的重构实践
你搜“SSH 远程控制 Windows 服务器”,十有八九会撞上一堆“用第三方软件”“改注册表强行开启”“PowerShell脚本一键安装但后续崩得莫名其妙”的教程。我干这行十多年,从最早用Cygwin模拟SSH服务,到后来部署Bitvise、FreeSSHD,再到2018年微软正式把OpenSSH Server作为可选功能集成进Windows 10/Server 2019,踩过的坑比走过的路还多。今天说的“如何使用 SSH 远程控制一台 Windows 服务器”,核心不是教你敲几条命令就完事——而是让你真正理解:Windows 的 SSH 不是 Linux 的复刻,它是一套以 PowerShell 为内核、以 Windows 安全模型为骨架、以 OpenSSH 协议为外壳的全新远程执行体系。这意味着,你不能照搬ssh user@ip就万事大吉;你得知道sshd进程在 Windows 上跑在哪一层服务模型里,C:\ProgramData\ssh\sshd_config里的ForceCommand和 Linux 的行为逻辑完全不同,$env:USERPROFILE在 PowerShell 会话里和 CMD 会话里指向的路径可能因启动方式而异。热搜词里反复出现的 “powershell -ep bypass”、“开机自启脚本”、“密钥登录失败但密码能通”,背后全是这套体系特有的权限链断裂、会话上下文丢失、UAC 隔离机制干扰问题。这篇文章不讲“能不能连上”,只讲“为什么连上后执行不了Get-Service”、“为什么scp传文件到C:\inetpub\wwwroot总提示拒绝访问”、“为什么用 VS Code Remote-SSH 打开终端,git status显示乱码”。它适合三类人:刚接手一台老版本 Windows Server 的运维要快速接管;开发团队想统一用 SSH 管理混合环境(Linux + Windows);还有那些被“PowerShell 脚本一键部署 OpenSSH”骗过、结果发现sshd服务根本起不来、日志里全是sshd: fatal: Unable to initialize registry key的人。下面所有内容,都来自我在金融、制造、政企客户现场真实部署 200+ 台 Windows Server(2012 R2 到 2022)的实操记录,每一步都有对应场景、错误日志和绕过方案。
2. 核心设计逻辑:为什么必须放弃“Linux思维”,转向“Windows安全上下文”模型
2.1 OpenSSH Server 在 Windows 上的本质:一个运行在 LocalSystem 上的“特权代理”,而非传统守护进程
很多人以为 Windows 上的sshd.exe和 Linux 的sshd是同一类东西——错了。Linux 的sshd是以root用户身份直接管理 TCP 连接、fork 子进程、切换用户 UID/GID;而 Windows 的sshd是一个Windows Service,默认以NT AUTHORITY\SYSTEM身份运行。这个身份拥有最高系统权限,但它不等于你登录后的用户会话。当你用ssh admin@192.168.1.100登录时,sshd接收连接后,并不会像 Linux 那样setuid()切换到admin用户,而是调用 Windows API 创建一个新的、隔离的交互式会话(Session 0 或 Session X),再在这个会话里启动powershell.exe或cmd.exe。这个过程涉及 Windows 的Session Isolation(会话隔离)、Window Station(窗口站)和Desktop(桌面对象)三层安全结构。这就是为什么你在 SSH 会话里执行Start-Process notepad.exe会失败——因为 Session 0 默认没有交互式桌面,notepad.exe需要 GUI 桌面句柄,而 SSH 启动的会话是“无桌面”的。这也是为什么ssh admin@server "net start w3svc"能成功,但ssh admin@server "start iis.msc"会报错“无法启动指定程序”。关键结论:Windows SSH 的核心能力边界,由 Windows 服务会话模型决定,而不是 OpenSSH 协议本身。你不能指望它像 Linux 那样自由 spawn GUI 进程或访问用户配置文件夹下的某些受保护资源。
2.2 PowerShell 作为默认 Shell 的深层意义:不是“换了个命令行”,而是“换了整套执行引擎”
微软官方文档写“OpenSSH Server on Windows uses PowerShell as the default shell”,但没告诉你这背后有多重含义。首先,sshd_config中的Subsystem sftp sftp-server.exe和ForceCommand指令,在 Windows 上的行为与 Linux 截然不同。Linux 的ForceCommand会强制替换掉用户指定的命令,而 Windows 的sshd对ForceCommand的处理更像一个“前置钩子”,它会在启动 PowerShell 前注入一段预设脚本,但不会阻止用户后续输入任意 PowerShell 命令。其次,PowerShell 的执行策略(Execution Policy)是独立于 SSH 会话存在的。你用管理员身份在本地打开 PowerShell,执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser,这个策略只对当前用户在本地会话生效;而 SSH 登录创建的是一个全新的、未加载任何用户配置的 PowerShell 会话,默认执行策略是Restricted。这就是为什么你ssh admin@server "Get-ExecutionPolicy"返回Restricted,哪怕你在本地已经设成RemoteSigned。更关键的是,PowerShell 的$PROFILE文件路径在 SSH 会话里是C:\Users\admin\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1,但sshd启动的会话默认不加载$PROFILE,除非你在sshd_config里显式配置ForceCommand powershell -NoProfile -ExecutionPolicy Bypass -Command "..."。所以,所谓“用 PowerShell 远程控制”,本质是“在一个受严格策略约束、无用户环境、无 GUI 上下文的最小化 PowerShell 实例中执行命令”。这不是便利,而是安全设计——它天然隔绝了本地用户配置对远程会话的污染,但也意味着你不能指望.bashrc那种自动 alias 加载。
2.3 密钥认证为何比密码更可靠?根源在于 Windows 的 CredSSP 与 Kerberos 认证链
热搜词里高频出现“ssh密钥”,但很少有人解释清楚:在 Windows 上,SSH 密钥认证的成功率远高于密码认证,其根本原因不在加密强度,而在Windows 的凭据委派(Credential Delegation)机制。当你用密码登录 SSH 时,sshd会调用 Windows 的LogonUserWAPI,传入明文密码进行验证。这个过程需要SE_TCB_PRIVILEGE(充当操作系统的一部分)权限,而默认情况下,NT AUTHORITY\SYSTEM账户拥有此权限,但如果服务器启用了“网络访问:不允许匿名 SID/名称转换”组策略,或者域控制器策略限制了 NTLM 认证,则LogonUserW会失败,返回ERROR_LOGON_FAILURE。而密钥认证走的是另一条路:sshd读取C:\ProgramData\ssh\administrators_authorized_keys(或用户目录下的authorized_keys),用公钥解密客户端发来的签名,验证通过后,直接调用CreateProcessAsUserWAPI,以目标用户身份创建新进程。这个 API 不依赖密码验证,只依赖 Windows 的S4U(Service-for-User)机制,它允许服务以用户身份启动进程,而无需用户提供密码。S4U 在域环境中与 Kerberos 紧密集成,在工作组环境中则依赖本地 SAM 数据库的缓存凭据。因此,密钥认证绕过了最脆弱的密码传输和 NTLM 验证环节,直击 Windows 身份验证的核心信任链。这也是为什么在企业内网,一旦域策略收紧,密码登录 SSH 经常失败,而密钥登录依然坚挺——它本质上是在利用 Windows 最底层的、为 Exchange、SQL Server 等关键服务设计的身份委派能力。
3. 实操全流程:从零部署到生产级稳定运行的七步法
3.1 环境确认与前置检查:别急着装,先看清楚你的 Windows 是“真·支持”还是“伪·支持”
很多故障源于第一步就错了。不是所有标着“Windows”的系统都能原生跑 OpenSSH Server。必须逐项核对:
- 操作系统版本:仅支持 Windows 10 1809(Build 17763)及更高版本,Windows Server 2019 及更高版本。
winver命令查看。特别注意:Windows Server 2012 R2 / 2016不原生支持,必须手动下载 OpenSSH for Windows(微软已停止维护旧版,强烈不建议)。 - OpenSSH 功能状态:以管理员身份打开 PowerShell,执行:
正常应返回两条:Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'OpenSSH.Client~~~~0.0.1.0和OpenSSH.Server~~~~0.0.1.0。如果只有 Client,说明 Server 功能未安装。如果返回空,说明系统版本太低或被禁用。 - 防火墙端口开放:默认 SSH 端口是 22,但很多企业防火墙默认关闭。检查命令:
如果无输出,需手动添加入站规则:Get-NetFirewallPortFilter | Where-Object LocalPort -eq 22New-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -DisplayName "OpenSSH Server (sshd)" -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 - 磁盘空间与权限:
C:\ProgramData\ssh目录必须存在且SYSTEM和Administrators组有完全控制权。曾遇到某台服务器因磁盘配额满导致sshd启动时无法写入sshd_config备份文件,服务卡在“正在启动”状态。用fsutil quota query C:检查配额状态。 - 防病毒软件拦截:某些国产杀软(如某360、某腾讯)会将
sshd.exe误判为“可疑远程控制程序”并静默终止。临时禁用杀软测试,若服务正常,则需在杀软白名单中添加C:\Windows\System32\OpenSSH\sshd.exe。
提示:以上五步缺一不可。我见过太多案例,客户说“装了但连不上”,结果发现是 Windows Server 2012 R2 强行装了旧版 OpenSSH,或者防火墙规则只开了出站没开入站,又或者杀软在后台默默 kill 进程。务必按顺序逐一验证,不要跳步。
3.2 安装与初始化:用 PowerShell 命令行而非图形界面,确保可审计、可回滚
图形界面安装(设置 > 应用 > 可选功能)看似简单,但存在两个致命缺陷:一是无法指定安装路径(必须是C:\Windows\System32\OpenSSH),二是无法在安装过程中修改默认配置。生产环境必须用 PowerShell 命令行安装,全程可记录、可脚本化:
# 1. 以管理员身份运行 PowerShell # 2. 安装 OpenSSH Server 功能(Client 已默认安装) Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 # 3. 启动 sshd 服务并设为自动启动 Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic' # 4. 初始化 sshd_config(关键!默认配置极度不安全) # 先备份原始配置 Copy-Item "C:\ProgramData\ssh\sshd_config" "C:\ProgramData\ssh\sshd_config.bak" -Force # 用 Here-String 重写配置,禁用密码登录、启用密钥、限制协议版本 $NewConfig = @" # Generated by PowerShell script on $(Get-Date) Port 22 Protocol 2 HostKey C:\ProgramData\ssh\ssh_host_rsa_key HostKey C:\ProgramData\ssh\ssh_host_dsa_key HostKey C:\ProgramData\ssh\ssh_host_ecdsa_key HostKey C:\ProgramData\ssh\ssh_host_ed25519_key # 禁用密码认证,强制密钥 PasswordAuthentication no PermitEmptyPasswords no # 禁用危险的认证方式 KerberosAuthentication no GSSAPIAuthentication no # 限制登录用户(可选,增强安全) AllowUsers admin deploy # 日志级别调高,便于排错 LogLevel VERBOSE # 关键:指定默认 Shell 为 PowerShell,且不加载 profile ForceCommand powershell -NoProfile -ExecutionPolicy Bypass -Command "if (`$args) { & `$args } else { powershell }" "@ $NewConfig | Out-File "C:\ProgramData\ssh\sshd_config" -Encoding utf8 -Force # 5. 重启服务使配置生效 Restart-Service sshd这段脚本的价值在于:它一次性完成安装、启动、配置、加固四件事,且所有操作都有明确日志(Get-EventLog -LogName System -Source sshd -After (Get-Date).AddMinutes(-5)可查)。ForceCommand行是精髓——它确保每个 SSH 会话都以干净、无 profile、无策略限制的 PowerShell 启动,避免了因用户 profile 加载失败导致的会话崩溃。AllowUsers行虽非必需,但在生产环境强烈建议启用,防止黑客爆破其他账户。
3.3 密钥对生成与部署:Windows 原生工具链 vs 第三方工具的实战对比
密钥是 Windows SSH 的生命线。必须用 Windows 原生工具生成,而非 PuTTYgen 或 OpenSSH for Linux:
# 在客户端(你的电脑)上,用 Windows 自带的 ssh-keygen ssh-keygen -t ed25519 -C "admin@prod-server" -f "$env:USERPROFILE\.ssh\id_ed25519_server" # 生成的私钥 id_ed25519_server 和公钥 id_ed25519_server.pub 都在 .ssh 目录下 # 将公钥内容复制(注意:是 .pub 文件内容,不是私钥!) Get-Content "$env:USERPROFILE\.ssh\id_ed25519_server.pub" | Set-Clipboard然后在服务器上部署公钥。这里有两种方式,推荐方式二:
方式一(官方推荐,但有坑):
# 在服务器上,以目标用户(如 admin)身份运行 mkdir C:\Users\admin\.ssh # 将公钥粘贴到 authorized_keys notepad C:\Users\admin\.ssh\authorized_keys # 保存后,修正权限(关键!) icacls C:\Users\admin\.ssh /inheritance:r icacls C:\Users\admin\.ssh /grant:r "admin:(RX)" icacls C:\Users\admin\.ssh\authorized_keys /grant:r "admin:(R)"坑点:icacls命令在某些 Windows 版本(尤其是 Server 2019)上,对authorized_keys文件的权限设置不彻底,导致sshd读取时仍报“Bad permissions”错误。
方式二(实测最稳,用 PowerShell 原生权限模块):
# 在服务器上,以管理员身份运行 $KeyPath = "C:\Users\admin\.ssh\authorized_keys" $KeyContent = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin@prod-server" # 粘贴你的公钥内容 $KeyContent | Out-File $KeyPath -Encoding utf8 -Force # 用 Set-Acl 彻底重置权限 $acl = Get-Acl $KeyPath $acl.SetAccessRuleProtection($true, $false) # 禁用继承,不清除现有规则 $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("admin","Read","Allow") $acl.SetAccessRule($rule) Set-Acl $KeyPath $acl # 同时修复 .ssh 目录权限 $DirAcl = Get-Acl "C:\Users\admin\.ssh" $DirAcl.SetAccessRuleProtection($true, $false) $dirRule = New-Object System.Security.AccessControl.FileSystemAccessRule("admin","ReadAndExecute","ContainerInherit,ObjectInherit","None","Allow") $DirAcl.SetAccessRule($dirRule) Set-Acl "C:\Users\admin\.ssh" $DirAcl这种方式用 .NET 的FileSystemAccessRule类精确控制 ACL,绕过了icacls的兼容性问题。实测在 Windows Server 2012 R2(需手动安装 OpenSSH)、2016、2019、2022 上全部通过。
3.4 连接测试与基础命令验证:不只是ssh admin@ip,而是验证整个执行链
连接成功不等于可用。必须分层验证:
基础连接与认证:
ssh -i ~/.ssh/id_ed25519_server -p 22 admin@192.168.1.100成功应显示 PowerShell 提示符
PS C:\Users\admin>。如果卡住或报错,立即查Get-WinEvent -LogName "OpenSSH/Admin"。Shell 环境验证:
# 在 SSH 会话中执行 $PSVersionTable.PSVersion # 确认是 PowerShell 5.1 或 7+ $env:COMPUTERNAME # 确认连接到了正确服务器 Get-ExecutionPolicy # 应为 Undefined 或 Bypass(因 ForceCommand 设置)关键命令执行验证:
# 测试无 GUI 的命令(应成功) Get-Service w3svc | Select-Object Status, Name # 测试需管理员权限的命令(应成功,因 SSH 会话以 admin 身份运行) netsh advfirewall firewall add rule name="Allow-SSH" dir=in action=allow protocol=TCP localport=22 # 测试文件操作(验证路径权限) echo "test" | Out-File C:\temp\ssh_test.txt -Encoding utf8 Get-Content C:\temp\ssh_test.txtSFTP 文件传输验证(常被忽略):
# 从客户端上传 scp -i ~/.ssh/id_ed25519_server -P 22 ./deploy.zip admin@192.168.1.100:C:\temp\ # 从客户端下载 scp -i ~/.ssh/id_ed25519_server -P 22 admin@192.168.1.100:C:\temp\log.txt ./SFTP 在 Windows 上默认使用
sftp-server.exe,它对路径权限极其敏感。如果C:\temp目录对admin用户没有写权限,scp会报错Permission denied,而非No such file or directory。这是判断权限配置是否正确的黄金测试。
3.5 生产级加固:从“能用”到“抗打”的五项必做配置
默认配置只能用于测试。上线前必须完成:
更改默认端口(非必须但强烈推荐):编辑
C:\ProgramData\ssh\sshd_config,将Port 22改为Port 2222(或其他高位端口),然后重启服务。此举可过滤掉 90% 的自动化扫描攻击。注意同步更新防火墙规则和客户端连接命令中的-p参数。禁用 root 登录等危险选项:在
sshd_config中确保:PermitRootLogin no PermitEmptyPasswords no UsePrivilegeSeparation sandbox StrictModes yes配置日志轮转与监控:Windows Event Log 默认不轮转。创建任务计划,每天凌晨执行:
# 保存为 rotate-ssh-log.ps1 wevtutil sl "OpenSSH/Admin" /ma:100MB wevtutil sl "OpenSSH/Operational" /ma:100MB并用
Get-WinEvent -LogName "OpenSSH/Admin" -MaxEvents 100 | Where-Object Id -eq 4监控认证失败事件,失败 5 次即触发告警。为特定用户创建专用密钥:不要让
admin账户的密钥到处用。为部署用户deploy创建专属密钥,并在sshd_config中用AllowUsers deploy锁定。密钥文件权限设为600(chmod 600在 Windows 上等价于icacls key -deny Everyone:(F))。禁用不安全的子系统:注释掉
sshd_config中的Subsystem sftp sftp-server.exe,如果不需要 SFTP。或者,将其替换为更安全的Subsystem sftp internal-sftp(需 OpenSSH 8.0+,Windows 2022 自带),并在Match User deploy块中限制其根目录:Match User deploy ChrootDirectory C:\sftp\%u ForceCommand internal-sftp AllowTcpForwarding no X11Forwarding no这样
deploy用户只能在C:\sftp\deploy目录下操作,无法逃逸。
3.6 VS Code Remote-SSH 集成:不只是“连上”,而是构建无缝开发体验
VS Code 的 Remote-SSH 扩展是 Windows 服务器开发的神器,但配置不当会导致“连接成功但无法加载扩展”、“终端乱码”、“Git 提交失败”。关键配置在~/.ssh/config:
Host win-prod HostName 192.168.1.100 User admin IdentityFile ~/.ssh/id_ed25519_server Port 2222 # 关键:指定 Windows 的 PowerShell 路径,避免找不到 RemoteCommand powershell.exe -NoProfile -ExecutionPolicy Bypass # 解决中文乱码(Windows 默认 GBK,VS Code 默认 UTF-8) SetEnv LANG=en_US.UTF-8 # 关键:禁用 VS Code 的自动 shell 检测,强制用 PowerShell RemoteShell powershell.exe然后在 VS Code 中按Ctrl+Shift+P,输入Remote-SSH: Connect to Host...,选择win-prod。首次连接会自动安装 VS Code Server 到C:\Users\admin\.vscode-server。如果卡在“Installing VS Code Server”,检查:
- 服务器
C:\Users\admin目录是否有写权限(icacls C:\Users\admin /grant admin:(OI)(CI)F) - 是否禁用了 Windows Defender 实时防护(它会扫描并阻塞
.vscode-server的 DLL 加载)
连接成功后,在 VS Code 终端中执行git config --global core.autocrlf true,解决 Windows/Linux 换行符差异。
3.7 故障排查与日志分析:读懂 Windows Event Log 里的“暗语”
当 SSH 连接失败,别急着重启服务。先看日志,Get-WinEvent是你的最佳朋友:
| 错误事件 ID | 日志来源 | 典型错误消息 | 根本原因 | 解决方案 |
|---|---|---|---|---|
| 4 | OpenSSH/Admin | User authentication failed | 密钥权限错误、authorized_keys路径不对、用户不存在 | 检查C:\Users\user\.ssh\authorized_keys权限,确认用户在AllowUsers列表中 |
| 11 | OpenSSH/Admin | sshd: fatal: Unable to initialize registry key | sshd_config文件编码错误(非 UTF-8)、语法错误 | 用 Notepad++ 以 UTF-8 无 BOM 重新保存sshd_config,用sshd -t测试语法 |
| 16 | OpenSSH/Admin | sshd: error: connect_to localhost port 22: failed. | ForceCommand指向的 Shell 不存在或路径错误 | 检查ForceCommand中的powershell.exe路径,确保C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe存在 |
| 20 | OpenSSH/Operational | Connection closed by authenticating user | 客户端密钥格式错误、服务器端sshd_config中PubkeyAuthentication设为no | 用ssh -vvv查看详细握手过程,确认PubkeyAuthentication yes已启用 |
一个真实案例:某客户报告“密钥能连,但执行Get-Service就断开”。查日志发现事件 ID 16,sshd尝试启动powershell.exe失败。深入排查发现,该服务器被组策略禁用了powershell.exe的执行(AppLocker规则)。解决方案不是改sshd_config,而是调整 AppLocker 策略,允许C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe。记住:Windows SSH 的绝大多数问题,根源都在 Windows 自身的安全策略(UAC、AppLocker、Group Policy、Antivirus),而非 OpenSSH 本身。
4. 常见问题与独家避坑指南:那些文档里不会写的“血泪经验”
4.1 “Connection refused” 的三种隐藏原因及秒级定位法
ssh: connect to host 192.168.1.100 port 22: Connection refused是最常见报错,但原因千差万别:
sshd服务根本没起来:不是“启动失败”,而是“从未启动”。执行Get-Service sshd | Select-Object Status, StartType。如果Status是Stopped且StartType是Disabled,说明安装时没设自动启动。执行Set-Service -Name sshd -StartupType Automatic后Start-Service sshd。端口被其他程序占用:Windows 的
netstat有时不准确。用更底层的命令:Get-NetTCPConnection -LocalPort 22 -State Listen | Select-Object LocalAddress, State, OwningProcess如果
OwningProcess是一个 PID,用Get-Process -Id <PID>查看是哪个进程。常见冲突程序:Skype(默认占 22 端口)、某些 P2P 软件、甚至 IIS 的 FTP 服务。Windows Firewall 的“域”、“专用”、“公用”配置不一致:图形界面里看到防火墙“已关闭”,但实际可能只关闭了“域”配置文件,而服务器处于“专用”网络。必须用 PowerShell 统一检查:
Get-NetFirewallProfile | Select-Object Name, Enabled确保
Domain,Private,Public三个 Profile 的Enabled都是False,或至少Private是True且有对应规则。
实操心得:我写了一个一键诊断脚本
ssh-diag.ps1,它会自动执行以上三步并输出结论。客户只需双击运行,就能知道是服务、端口还是防火墙的问题。脚本核心就是这三行命令的组合,比任何 GUI 工具都快。
4.2 “Permission denied (publickey)” 的七层穿透排查法
这个错误看似简单,实则涉及七层验证:
| 层级 | 检查点 | 验证命令 | 通过标志 |
|---|---|---|---|
| L1 客户端密钥 | 私钥文件是否存在、权限是否 600 | ls -l ~/.ssh/id_ed25519_server | -rw------- |
| L2 客户端配置 | ssh -i指定的路径是否正确 | ssh -i ~/.ssh/wrong_key admin@server | 报错Load key ...: invalid format |
| L3 服务器密钥存储 | authorized_keys文件是否存在 | Test-Path C:\Users\admin\.ssh\authorized_keys | True |
| L4 服务器密钥权限 | authorized_keys文件 ACL 是否正确 | icacls C:\Users\admin\.ssh\authorized_keys | 包含admin:(R) |
| L5 服务器用户权限 | admin用户对.ssh目录是否有读权限 | icacls C:\Users\admin\.ssh | 包含admin:(RX) |
| L6 服务器配置开关 | sshd_config中PubkeyAuthentication是否为yes | Select-String "PubkeyAuthentication" C:\ProgramData\ssh\sshd_config | 输出PubkeyAuthentication yes |
| L7 服务器密钥解析 | sshd是否能正确读取公钥格式 | sshd -T | findstr "pubkey" | 输出pubkeyacceptedkeytypes=ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa |
独家技巧:当 L1-L6 都确认无误,L7 却失败时,大概率是公钥格式问题。Windows 的sshd对ssh-ed25519公钥的 Base64 编码要求极其严格。用ssh-keygen -lf ~/.ssh/id_ed25519_server.pub查看指纹,如果报错invalid format,说明公钥损坏。此时不要重生成,而是用ssh-keygen -e -f ~/.ssh/id_ed25519_server.pub -m PEM > temp.pub转换格式,再将temp.pub内容复制到服务器authorized_keys。
4.3 PowerShell 执行策略与 SSH 会话的“隐形战争”
ExecutionPolicy是 Windows SSH 最隐蔽的坑。你ssh admin@server "Get-ExecutionPolicy"得到Undefined,但执行Invoke-Expression (Invoke-WebRequest https://example.com/script.ps1)却报错File xxx.ps1 cannot be loaded because running scripts is disabled...。这是因为:
Get-ExecutionPolicy返回的是当前作用域的策略,而Invoke-Expression加载的远程脚本,其策略由Set-ExecutionPolicy的-Scope参数决定。- SSH 会话的默认作用域是
Process,而Process作用域的策略优先级高于CurrentUser和LocalMachine。
终极解决方案:在sshd_config的ForceCommand中,强制指定执行策略:
ForceCommand powershell -NoProfile -ExecutionPolicy Unrestricted -Command "if (`$args) { & `$args } else { powershell }"Unrestricted是唯一能保证所有脚本(包括从网络下载的)都能执行的策略。虽然听起来不安全,但请记住:SSH 会话本身已是强认证通道,且ForceCommand已将用户锁定在 PowerShell 环境中,风险可控。比它更不安全的是让用户自己在会话里执行Set-ExecutionPolicy,那会永久修改用户策略。
4.4 SFTP 上传失败的“路径陷阱”:Windows 权限模型的终极考验
scp file.zip admin@server:C:\inetpub\wwwroot\报错Permission denied,但ssh admin@server "mkdir C:\inetpub\wwwroot\test"却成功。这是因为:
mkdir命令由sshd启动的 PowerShell 进程执行,该进程以admin用户身份运行,拥有C:\inetpub\wwwroot的写权限(假设已赋权)。scp上传由sftp-server.exe进程执行,它是一个独立的、以SYSTEM身份运行的子进程,不继承 SSH 会话的用户权限。sftp-server.exe尝试以SYSTEM身份写入C:\inetpub\wwwroot,而SYSTEM默认对此目录只有读权限。
破解方法:给C:\inetpub\wwwroot目录显式添加NT AUTHORITY\SYSTEM的写权限:
icacls "C:\inetpub\wwwroot" /grant "NT AUTHORITY\SYSTEM:(OI)(CI)F" /T/T参数递归应用到所有子目录和文件。这是 SFTP 在 Windows 上工作的基石——你必须为sftp-server.exe的运行身份(SYSTEM)单独授权,而不是为 SSH 登录用户授权。
4.5 VS Code Remote-SSH 的“中文乱码”终极修复
在 VS Code 终端中ls显示??????.txt,git status显示# ?? ????????.txt。这不是 VS Code 的 bug,而是 Windows 控制台编码与 VS Code 的 UTF-8 解码不匹配。
三步修复法:
- 服务器端设置 PowerShell 默认编码:
这行代码确保 PowerShell 启动时# 在服务器上,以 admin 用户身份运行 $profilePath = "C:\Users\admin\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" "if (`$Host.UI.SupportsVirtualTerminal) { `$Host.UI.RawUI.UnicodeEncoding = [System.Text.Encoding]::UTF8 }" | Out-File $profilePath -Append -Encoding utf8