上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器运行 Windows Server 2022 时,用 PowerShell 远程会话优化跨境自动化运维

发布人:Minchunlin 发布时间:2025-09-03 09:01 阅读量:713


那天是周三,23:47。我正准备关电脑,电话响了:香港机房的一批 Windows Server 2022 要在凌晨时段补丁维护,同时还要下发新的监控 Agent。问题不大,但 Ops 小伙伴在内地办公室,跨境链路偶尔抖动,之前的脚本总是卡在 New-PSSession 这一步。

我背上双肩包赶到机房的运维间,空调嗡嗡响。打卡、开屏、插上随身的 2.5G USB 网卡,开始把“远程会话这件小事儿”一次性掰正。

先说结论:跨境(内地 ↔ 香港)远程运维的“顺滑感”,80% 取决于 WinRM/PSRP 的链路质量 + 证书与鉴权方式 + 会话参数三件事。把它们一次性配到位,再加上 JEA 最小权限与审计,PowerShell 的远程自动化就像一条打磨好的生产线,稳、快、可追溯。

环境与目标

目标

  • 统一:通过 PowerShell Remoting(PSRP over WinRM/HTTPS)统一管理香港机房的 Windows Server 2022。
  • 安全:全链路 TLS、最小权限(JEA)、操作可审计。
  • 跨境优化:高 RTT、偶发丢包下仍能稳定批量下发任务。
  • 可复制:写成可复用的“远程会话基线 + 自动化脚本模板”。

基本架构与硬件示例(可替换为你的实际环境)

角色 规格/版本 关键点
管理端(内地办公网) Windows 11 + PowerShell 7.4 / 5.1 出口走企业代理;建议能直出 443
跳板机(香港) Windows Server 2022 Datacenter;NIC: Intel X710 10GbE;NVMe: 1TB 公网 IP+WAF;同时跑 Nginx/HAProxy 端口映射
被管节点(香港) Windows Server 2022 Standard/Datacenter x N 加域或本地工作组均可
证书 Let’s Encrypt / 企业 CA / 自签名(临时) 优先公信 CA;自签需妥善分发根证书
带宽/RTT 内地 ↔ 香港 RTT 35–75ms 丢包 <1% 常见,偶发 >2%

方案选型与关键决策

  • 传输协议:WinRM/HTTPS(5986)为主;如办公网仅放行 443,则在香港跳板用 Nginx/HAProxy 做 443 → 5986 的 TCP 转发。
  • 鉴权:优先 Negotiate/Kerberos(域内),跨网或非域场景走 Basic over TLS 或 证书映射。
  • 最小权限:使用 JEA(Just Enough Administration) 发布受限的远程端点。
  • PowerShell 版本:管理端推荐 PowerShell 7+;服务端保留 5.1 以兼容 WinRM/PSRP。
  • 稳定性优化:合理调大 IdleTimeout / OperationTimeout / MaxEnvelopeSizekb,并并发 + 幂等的任务设计。

第一步:被管节点的 WinRM/HTTPS 基线

1)启用 PS Remoting 与网络侧准备(在被管节点执行,管理员权限)

# 设为私有网络(公共网络会阻挡 WinRM 规则)
Get-NetConnectionProfile
Set-NetConnectionProfile -InterfaceAlias "Ethernet" -NetworkCategory Private

# 开启 PS Remoting(自动放行防火墙规则)
Enable-PSRemoting -Force

# 仅允许加密(禁用未加密)
Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $false

# 开启 Negotiate,按需考虑禁用 Basic
Set-Item -Path WSMan:\localhost\Service\Auth\Negotiate -Value $true
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $false  # 若跨域且必须 Basic,请保留 $true 但必须配合 HTTPS

# 查看侦听端口(此时只有 HTTP 5985)
winrm enumerate winrm/config/listener

2)为 WinRM 配置 HTTPS 监听与证书

优先使用公信 CA(如 Let’s Encrypt / 企业 CA)。如需临时自签:

# 生成自签证书(替换为你的 FQDN)
$fqdn = "hk-mgmt.example.com"
$cert = New-SelfSignedCertificate `
  -DnsName $fqdn `
  -CertStoreLocation Cert:\LocalMachine\My `
  -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256 `
  -NotAfter (Get-Date).AddYears(2)

# 取指纹,创建 HTTPS 监听(5986)
$thumb = $cert.Thumbprint
winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$fqdn`"; CertificateThumbprint=`"$thumb`"}"

# 放行防火墙
New-NetFirewallRule -DisplayName "WinRM over HTTPS 5986" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 5986

若办公侧只放行 443:在香港跳板机部署 Nginx/HAProxy 四层转发 443 → 5986(同机/旁路均可)。示例(Nginx stream):

stream {
    upstream winrm_https {
        server 127.0.0.1:5986; # 或者内网被管节点的 5986
    }
    server {
        listen 443;
        proxy_pass winrm_https;
    }
}

3)合理的 WinRM/PSRP 参数(在被管节点)

注意:以下为跨境场景“起步值”,务必按你实际 RTT/丢包做 A/B 调整。

# 执行时间更宽容(默认 60s → 180s)
Set-Item -Path WSMan:\localhost\Service\MaxTimeoutms -Value 180000

# 放大单封包大小(默认 500KB → 8MB,用于较大脚本/对象)
winrm set winrm/config @{MaxEnvelopeSizekb="8192"}

# 增大空闲断开时间(默认 15min → 2h;单位 ms)
Set-Item -Path WSMan:\localhost\Shell\IdleTimeout -Value 7200000

# 提升每 Shell 可用内存(默认 512MB;按需提高)
Set-Item -Path WSMan:\localhost\Shell\MaxMemoryPerShellMB -Value 1024

第二步:管理端的会话与证书信任

1)导入根证书(仅自签场景)

从服务器导出根证书(或你的企业 CA 根),在管理端导入到 受信任的根证书颁发机构。

避免 The SSL certificate is not trusted 的连接失败。

2)构建稳健的会话选项(管理端执行)

$so = New-PSSessionOption `
  -OpenTimeout 180000 `          # 打开会话容忍 180s
  -OperationTimeout 180000 `     # 每个命令容忍 180s
  -IdleTimeout 7200000           # 2 小时空闲不踢

3)创建远程会话(域内 Negotiate;非域用 Basic over TLS)

# 域内(推荐)
$s = New-PSSession -ComputerName hk-mgmt.example.com -UseSSL -SessionOption $so

# 非域(或跨网不可 Kerberos,谨用 Basic 但务必在 HTTPS 上)
$cred = Get-Credential
$s = New-PSSession -ComputerName hk-mgmt.example.com -UseSSL -Authentication Basic -Credential $cred -SessionOption $so

第三步:JEA 最小权限与审计

1)角色能力(Role Capability)

# 模块与角色能力目录
$moduleRoot = "C:\Program Files\WindowsPowerShell\Modules\HKOps"
New-Item -ItemType Directory -Force -Path "$moduleRoot\RoleCapabilities" | Out-Null

# 建一个只允许常用安全运维命令的角色
$newRole = "$moduleRoot\RoleCapabilities\OpsOperators.psrc"
New-PSRoleCapabilityFile -Path $newRole -VisibleCmdlets @(
  'Get-Service','Restart-Service','Get-Process','Stop-Process',
  'Get-EventLog','Get-WinEvent','Get-ChildItem','Get-Item','Set-Item',
  'Test-Connection','Test-NetConnection','Get-Content','Set-Content'
) -VisibleFunctions @() -VisibleExternalCommands @() -Description "Ops 基础只读/有限操作" -Author "SRE" -CompanyName "Example"

2)会话配置(PSSC)

$pssc = "C:\ProgramData\JEA\HKOps_Endpoint.pssc"
New-Item -ItemType Directory -Force -Path (Split-Path $pssc) | Out-Null

New-PSSessionConfigurationFile `
  -Path $pssc `
  -SessionType RestrictedRemoteServer `
  -TranscriptDirectory "C:\JEA\Transcripts" `
  -RunAsVirtualAccount `
  -RoleDefinitions @{
    "EXAMPLE\Ops" = @{ RoleCapabilities = 'OpsOperators' }  # 域组/本地组均可
  }

# 注册 JEA 端点
Register-PSSessionConfiguration -Name "HKOps" -Path $pssc -Force

# 验证
Get-PSSessionConfiguration | where Name -eq "HKOps"

使用:

Enter-PSSession -ComputerName hk-mgmt.example.com -UseSSL -ConfigurationName HKOps

收益:就算凭证泄露,也只能在受限端点内执行有限命令;所有命令自动留痕到 C:\JEA\Transcripts。

第四步:跨境稳定性与并发策略

1)链路探测与基线

Test-NetConnection hk-mgmt.example.com -Port 5986
Test-WSMan hk-mgmt.example.com -UseSSL

2)并发分组与幂等

  • 并发批次:分组每批 10–20 台,-ThrottleLimit 10/20。
  • 幂等脚本:反复执行不产生副作用(先检查状态再变更)。
  • 大对象传输:优先使用文件分发(Copy-Item -ToSession),避免把大对象作为返回值。

示例:批量分发 Agent + 校验

$targets = Get-Content .\hk_nodes.txt
$cred = Get-Credential
$so = New-PSSessionOption -OpenTimeout 180000 -OperationTimeout 180000 -IdleTimeout 7200000

$sessions = New-PSSession -ComputerName $targets -UseSSL -Credential $cred -Authentication Basic -SessionOption $so -ErrorAction SilentlyContinue

# 分发安装包
$sessions | ForEach-Object {
  Copy-Item -ToSession $_ -Path .\agent\agent.msi -Destination "C:\Temp\agent.msi"
}

# 安装 + 验证(并发执行)
Invoke-Command -Session $sessions -ScriptBlock {
  $msi = "C:\Temp\agent.msi"
  if (Test-Path $msi) {
    Start-Process msiexec.exe -ArgumentList "/i `"$msi`" /qn" -Wait
  }
  # 幂等校验
  Get-Service -Name "AgentSvc" -ErrorAction SilentlyContinue | Select-Object Name,Status,StartType
} -ThrottleLimit 15

第五步:日志与审计

打开 PowerShell Operational 日志:

Event Viewer → Applications and Services Logs → Microsoft → Windows → PowerShell → Operational

JEA 转录:操作在 C:\JEA\Transcripts(可配合集中收集)。

服务器侧 WinRM 日志:

Event Viewer → Applications and Services Logs → Microsoft → Windows → Windows Remote Management

关键参数建议(跨境初始值)

建议值 说明
Service.MaxTimeoutms 180000 高 RTT/丢包时给命令足够时间
Shell.IdleTimeout 7200000 长批处理不中断
MaxEnvelopeSizekb 8192 大脚本/结果集避免 500 错
MaxMemoryPerShellMB 1024 复杂对象/并发更稳
ThrottleLimit 10–20 分批并发,避免拥塞

微调方法:用 5 台、10 台、20 台分批执行同一脚本,记录耗时与失败率,逐步调参。

真实观测数据(一组样例)

场景 RTT (ms) 丢包 批量规模 平均执行时延 失败率
默认参数(未调优) 55 0.5% 20 18m32s 7%
调整 Timeout/Envelope 55 0.5% 20 14m05s 2%
分批+并发 10 55 0.5% 60(分3批) 27m50s 1%
转发到 443(WAF 旁路) 60 1.0% 20 15m40s 3%

样例仅用于说明调参方向;你的网络与负载不同,结果会不同。

常见坑与现场解法

“WinRM firewall exception will not work because one of the network connection types is Public”

原因:网络配置为 Public。

解法:Set-NetConnectionProfile -NetworkCategory Private 或 Enable-PSRemoting -SkipNetworkProfileCheck(不推荐)。

New-PSSession 报 SSL 证书不受信

原因:自签或证书 CN/SAN 不匹配。

解法:优先公信 CA;自签需导入根证书;确保证书 SAN 含 FQDN。

HTTP 500 / MaxEnvelopeSize exceeded

原因:返回对象太大或脚本/结果集过大。

解法:MaxEnvelopeSizekb 提到 8192;或输出落盘 + 拉取文件。

NTLM/双跳问题(访问第三方资源失败)

原因:远程到 A 上再访问 B,无委派。

解法:用 CredSSP 或 Kerberos 约束委派;或改造为从管理端直连 B;或 JEA RunAs Virtual Account + 资源 ACL。

办公侧只放行 443,5986 受限

解法:香港跳板上跑 Nginx/HAProxy 做 443 → 5986 四层转发;或 SLB/防火墙 DNAT。

跨境偶发卡顿

解法:会话选项调大 OpenTimeout/OperationTimeout;任务拆小、支持断点续跑、失败重试(指数退避)。

现场自动化“模板化”示例

1)通用远程执行器(带重试)

function Invoke-Remote {
  param(
    [string[]]$Computers,
    [scriptblock]$Script,
    [int]$Throttle = 15,
    [int]$Retry = 2,
    [System.Management.Automation.PSCredential]$Credential
  )
  $so = New-PSSessionOption -OpenTimeout 180000 -OperationTimeout 180000 -IdleTimeout 7200000
  $sessions = $null
  try {
    $sessions = New-PSSession -ComputerName $Computers -UseSSL -Authentication Basic -Credential $Credential -SessionOption $so -ErrorAction SilentlyContinue
    for($i=0; $i -le $Retry; $i++){
      $failed = @()
      Invoke-Command -Session $sessions -ScriptBlock $Script -ThrottleLimit $Throttle -ErrorAction SilentlyContinue -ErrorVariable ev
      if($ev){
        $failed = $ev[0].TargetObject.ComputerName
        if($failed.Count -gt 0 -and $i -lt $Retry){
          Start-Sleep -Seconds ([math]::Pow(2,$i+1))
          $sessions = New-PSSession -ComputerName $failed -UseSSL -Authentication Basic -Credential $Credential -SessionOption $so -ErrorAction SilentlyContinue
        }
      } else { break }
    }
  } finally {
    if($sessions){ $sessions | Remove-PSSession }
  }
}

2)一键打补丁(幂等)

$targets = Get-Content .\hk_nodes.txt
$cred = Get-Credential
$script = {
  Install-WindowsUpdate -AcceptAll -AutoReboot:$false -ErrorAction SilentlyContinue
  Get-WindowsUpdateLog | Out-Null
  (Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 5) | Format-Table -AutoSize
}
Invoke-Remote -Computers $targets -Script $script -Throttle 10 -Retry 2 -Credential $cred

注:需要提前分发 PSWindowsUpdate 模块或改用原生命令组合。

3)基线核查(示例)

$check = {
  [pscustomobject]@{
    Name = $env:COMPUTERNAME
    WinRM_HTTPS = (winrm enumerate winrm/config/listener | Select-String "Transport = HTTPS") -ne $null
    TLS_Only    = -not (Get-Item WSMan:\localhost\Service\AllowUnencrypted).Value
    IdleTimeout = (Get-Item WSMan:\localhost\Shell\IdleTimeout).Value
  }
}
Invoke-Remote -Computers (Get-Content .\hk_nodes.txt) -Script $check -Throttle 20 -Retry 1 -Credential (Get-Credential)

与 OpenSSH Remoting 的取舍(可选)

  • 优点:端口 22 更容易被允许;PowerShell 7 原生 Enter-PSSession -HostName。
  • 缺点:与 WinRM/JEA/Windows 审计的生态稍有割裂;证书与密钥管理另起炉灶。
  • 建议:在极端端口受限环境可作为 备用通道,主通道仍以 WinRM/HTTPS + JEA 为主。

复盘:一夜之间把“会话通道”打通

那天凌晨,我们先把跳板的 443 → 5986 转发架起来,又统一了证书与 WinRM 的基线。
第二批脚本下发时,监控板上的红点没再跳,RTT 偶尔飘动也不影响任务推进。凌晨 3:10,最后一台节点完成补丁,JEA 转录里留好了每一步的“指纹”。
我合上笔记本,走出机房时,冷气换成了湿热的海风。脑子里只有一个想法:把这套远程会话基线固化成文档和脚本,后面的人不再为它熬夜。

目录结构
全文