Windows原生SSH远程控制:PowerShell会话模型与安全上下文实战

发布时间:2026/8/23 1:03:40
Windows原生SSH远程控制:PowerShell会话模型与安全上下文实战 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 userip就万事大吉你得知道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 Server2012 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 admin192.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 adminserver net start w3svc能成功但ssh adminserver 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 adminserver 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 的S4UService-for-User机制它允许服务以用户身份启动进程而无需用户提供密码。S4U 在域环境中与 Kerberos 紧密集成在工作组环境中则依赖本地 SAM 数据库的缓存凭据。因此密钥认证绕过了最脆弱的密码传输和 NTLM 验证环节直击 Windows 身份验证的核心信任链。这也是为什么在企业内网一旦域策略收紧密码登录 SSH 经常失败而密钥登录依然坚挺——它本质上是在利用 Windows 最底层的、为 Exchange、SQL Server 等关键服务设计的身份委派能力。3. 实操全流程从零部署到生产级稳定运行的七步法3.1 环境确认与前置检查别急着装先看清楚你的 Windows 是“真·支持”还是“伪·支持”很多故障源于第一步就错了。不是所有标着“Windows”的系统都能原生跑 OpenSSH Server。必须逐项核对操作系统版本仅支持 Windows 10 1809Build 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 22如果无输出需手动添加入站规则New-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 adminprod-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... adminprod-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 adminip而是验证整个执行链连接成功不等于可用。必须分层验证基础连接与认证ssh -i ~/.ssh/id_ed25519_server -p 22 admin192.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 nameAllow-SSH dirin actionallow protocolTCP localport22 # 测试文件操作验证路径权限 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 admin192.168.1.100:C:\temp\ # 从客户端下载 scp -i ~/.ssh/id_ed25519_server -P 22 admin192.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锁定。密钥文件权限设为600chmod 600在 Windows 上等价于icacls key -deny Everyone:(F)。禁用不安全的子系统注释掉sshd_config中的Subsystem sftp sftp-server.exe如果不需要 SFTP。或者将其替换为更安全的Subsystem sftp internal-sftp需 OpenSSH 8.0Windows 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/configHost 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 默认 GBKVS Code 默认 UTF-8 SetEnv LANGen_US.UTF-8 # 关键禁用 VS Code 的自动 shell 检测强制用 PowerShell RemoteShell powershell.exe然后在 VS Code 中按CtrlShiftP输入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日志来源典型错误消息根本原因解决方案4OpenSSH/AdminUser authentication failed密钥权限错误、authorized_keys路径不对、用户不存在检查C:\Users\user\.ssh\authorized_keys权限确认用户在AllowUsers列表中11OpenSSH/Adminsshd: fatal: Unable to initialize registry keysshd_config文件编码错误非 UTF-8、语法错误用 Notepad 以 UTF-8 无 BOM 重新保存sshd_config用sshd -t测试语法16OpenSSH/Adminsshd: error: connect_to localhost port 22: failed.ForceCommand指向的 Shell 不存在或路径错误检查ForceCommand中的powershell.exe路径确保C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe存在20OpenSSH/OperationalConnection closed by authenticating user客户端密钥格式错误、服务器端sshd_config中PubkeyAuthentication设为no用ssh -vvv查看详细握手过程确认PubkeyAuthentication yes已启用一个真实案例某客户报告“密钥能连但执行Get-Service就断开”。查日志发现事件 ID 16sshd尝试启动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 客户端密钥私钥文件是否存在、权限是否 600ls -l ~/.ssh/id_ed25519_server-rw-------L2 客户端配置ssh -i指定的路径是否正确ssh -i ~/.ssh/wrong_key adminserver报错Load key ...: invalid formatL3 服务器密钥存储authorized_keys文件是否存在Test-Path C:\Users\admin\.ssh\authorized_keysTrueL4 服务器密钥权限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是否为yesSelect-String PubkeyAuthentication C:\ProgramData\ssh\sshd_config输出PubkeyAuthentication yesL7 服务器密钥解析sshd是否能正确读取公钥格式sshd -T | findstr pubkey输出pubkeyacceptedkeytypesecdsa-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 adminserver 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 adminserver:C:\inetpub\wwwroot\报错Permission denied但ssh adminserver 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显示??????.txtgit status显示# ?? ????????.txt。这不是 VS Code 的 bug而是 Windows 控制台编码与 VS Code 的 UTF-8 解码不匹配。三步修复法服务器端设置 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这行代码确保 PowerShell 启动时