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

如何在香港服务器的Windows Server系统中配置Just Enough Administration (JEA),减少权限滥用风险?

发布人:Minchunlin 发布时间:2025-08-21 09:55 阅读量:724


夜半两点,香港荔枝角机房的值班群里“叮”地一声:有一台前端节点 CPU 飙高、IIS 停摆。年轻同事小李第一反应是拿着域管凭据 RDP 上去——十分钟后服务是起来了,但第二天我在变更审计里看见:他顺手“优化”了几个计划任务,还把一个旧版脚本删了。业务没受影响,心却凉了半截——这已经不是第一次“为了快”而越权。就在那一刻,我下定决心,把香港区的运维入口全部收束到 JEA(Just Enough Administration) 上:不给不该给的权限,也不给“顺手做点别的”的机会。

下面这份笔记,就是我在香港机房落地 JEA 的完整过程。它不是泛泛而谈的白皮书,而是带有灰尘、指纹和坑洞的真实记录:从规划、落地到排坑;从硬件、网络到策略、脚本;新手能照做,老手也能挑刺。

我们的现场与边界条件

机房概况(HK1 机房,B 区列头柜)

规格/现状
机型 Dell R650 / HPE DL360 混部
CPU / 内存 Xeon Silver/Gold(12–24C)/ 128–256 GB
存储 系统盘 SATA SSD + 数据盘 NVMe U.2(RAID1/10)
网卡 2×10GbE(生产/管理面分离)
OS Windows Server 2019/2022 Datacenter(GUI + 少量 Core)
PowerShell 5.1(内置)+ 7.4(部分运维机)
身份 单域(hk.example.local),集中 AD
访问 跳板/Bastion + WinRM/PowerShell Remoting(强制 HTTPS
审计 Windows 事件日志 + PowerShell Transcript + SIEM 汇聚

目标

  • 把“会的人”变成“会 + 只会做该做的事的人”。
  • 修复“救火=越权”的文化,固化到入口与工具链里。
  • 减少域管/本地管理员的流转,最小权限、可审计、可回滚。

为什么是 JEA(而不是单纯靠制度/脚本)

  • 最小暴露面:JEA 用受限端点暴露“刚好够”的命令/参数/对象,跑在 RunAs 虚拟账户或受控服务账户之下。
  • 黑匣子审计:每个会话强制转录(Transcript)、事件日志齐全;谁什么时候在端点里干了什么,一清二楚。
  • 好落地:Windows Server 2016+ 原生能力;服务端坚持用 Windows PowerShell 5.1 + WinRM,客户端可用 5.1 或 7.x。
  • 可组合:角色能力(Role Capability)像乐高一样拼装,能适配我们 HK 区“Web 维护 / SQL 只读 / 巡检”等典型场景。

角色设计与权限矩阵(先定边界,再写配置)

角色 典型操作 暴露命令/限制 RunAs 身份 备注
WebMaintainer 重启 AppPool、站点状态、导出 IIS 日志 Restart-WebAppPool(仅指定池名)、Get-WebSiteGet/Restart-Service(仅 W3SVC)等 本地虚拟账户 + Administrators(仅此端点内生效) 避免给本地管理员实权,靠端点限制命令面
SQLReadOnly 查只读 DMV、导出备份状态 Invoke-Sqlcmd(只读实例/数据库)、Get-Service(SQL 服务状态) 受限域组(DB 只读) 若需跨机资源,使用专用服务账户端点
SysInspector 查事件、系统信息、磁盘、网卡 Get-EventLog/Get-WinEventGet-ProcessGet-NetAdapterGet-Volume 本地虚拟账户 + Event Log Readers 只读巡检,无写操作
EmergencyReset 重置某些服务、清理特定目录 Restart-Service(白名单)、Remove-Item(指定路径、阻止 -Recurse) 本地虚拟账户 + Administrators 仅应急窗口开放

原则

  • “能做成事”的最小集合;每个命令都配参数白名单。
  • 涉及跨主机/网络资源的场景,单独做“服务账户端点”(避免虚拟账户的网络不可见限制)。

前置准备(一次性)

1) WinRM/PowerShell Remoting(HTTPS 强制)

# 在目标服务器(示例:HK-WEB-01)上
Enable-PSRemoting -Force

# 申请/生成证书(建议用企业 CA;这里示例自签)
$cert = New-SelfSignedCertificate -DnsName "hk-web-01.hk.example.local" `
  -CertStoreLocation Cert:\LocalMachine\My
$thumb = $cert.Thumbprint

# 创建 HTTPS Listener
winrm create winrm/config/Listener?Address=*+Transport=HTTPS `
"@{Hostname=`"hk-web-01.hk.example.local`"; CertificateThumbprint=`"$thumb`"}"

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

经验:跨机房/跨运营商环境下,只开 5986(HTTPS),并把 WinRM 限制在管理网段;证书务必匹配主机名,否则客户端会抛证书错误。

2) 目录与审计

New-Item -ItemType Directory -Path C:\JEA\Transcripts -Force | Out-Null
# 建议组策略同时开启 Script Block Logging + Module Logging

动手:以“WebMaintainer”端点为例(IIS 运维)

我们把所有 IIS 维护动作集中到一个 JEA 端点 WebMaintenance 里,授权给域组 HK\IIS-Ops。

Step 1:创建模块骨架(承载 Role Capability)

$ModuleName = 'HKG.JEA.WebOps'
$ModuleVer  = '1.0.0'
$ModuleRoot = "C:\Program Files\WindowsPowerShell\Modules\$ModuleName\$ModuleVer"

New-Item -ItemType Directory -Path "$ModuleRoot\RoleCapabilities" -Force | Out-Null

New-ModuleManifest -Path "$ModuleRoot\$ModuleName.psd1" `
  -RootModule '' `
  -Author 'HK Ops' -CompanyName 'Example Ltd' `
  -ModuleVersion $ModuleVer `
  -Description 'JEA RoleCapabilities for HK Web operations'

经验:RoleCapability 文件必须放在 …\Modules\<Module>\RoleCapabilities\ 下,文件名与能力名一致,否则 JEA 找不到。

Step 2:写 Role Capability(IISMaintainer.psrc)

$rcPath = "$ModuleRoot\RoleCapabilities\IISMaintainer.psrc"

New-PSRoleCapabilityFile -Path $rcPath `
  -Author 'HK Ops' -CompanyName 'Example Ltd' `
  -Description 'Allow safe IIS maintenance operations' `
  -ModulesToImport @('WebAdministration') `
  -VisibleCmdlets @(
    @{ Name = 'Get-Website' },
    @{ Name = 'Get-WebAppPoolState' },
    @{ Name = 'Restart-WebAppPool';
       Parameters = @{ Name='Name'; ValidateSet=@('DefaultAppPool','ApiPool','PortalPool') } },
    @{ Name = 'Get-Service';
       Parameters = @{ Name='Name';  ValidateSet=@('W3SVC','WAS') } },
    @{ Name = 'Restart-Service';
       Parameters = @{ Name='Name';  ValidateSet=@('W3SVC','WAS') } },
    # 只读系统信息
    'Get-Process','Get-ComputerInfo','Get-ChildItem','Get-Item','Test-Path'
  ) `
  -VisibleProviders @('FileSystem','Registry') `
  -AliasesToExport @()

经验:把 Restart-Service 这类“危险但必要”的 cmdlet 用 ValidateSet 锁死到白名单服务;不要暴露 Stop-Process、Remove-Item -Recurse 等可破坏性强的命令。

Step 3:写 Session Configuration(WebMaintenance.pssc)

$pssc = 'C:\JEA\WebMaintenance.pssc'

New-PSSessionConfigurationFile -Path $pssc `
  -SchemaVersion '2.0.0.0' `
  -SessionType RestrictedRemoteServer `
  -LanguageMode NoLanguage `
  -TranscriptDirectory 'C:\JEA\Transcripts' `
  -RunAsVirtualAccount `
  -RunAsVirtualAccountGroups @('Administrators') `
  -ModulesToImport @('WebAdministration') `
  -RoleDefinitions @{
     'HK\IIS-Ops' = @{ RoleCapabilities = 'IISMaintainer' }
  }

解释:

  • RunAsVirtualAccount:会话内会生成一个临时本地“虚拟管理员”,它只在这个端点中有效。结合受限命令面,既“拿得到权限”,又“用不出祸”。
  • NoLanguage:禁用脚本语言特性(如 if/foreach),进一步缩小攻击面。
  • TranscriptDirectory:所有会话完整转录,便于审计/回溯。

Step 4:注册端点

Register-PSSessionConfiguration -Name 'WebMaintenance' -Path $pssc -Force
# 生效验证
Get-PSSessionConfiguration -Name WebMaintenance | Format-List Name,Permission,StartupScript

Step 5:授权与连通性测试

# 把需要做 IIS 维护的同事加入域组 HK\IIS-Ops(由 AD 管理同学操作)
# 客户端(跳板机或本机 PowerShell 5.1/7.x 均可)
Enter-PSSession -ComputerName hk-web-01.hk.example.local `
  -ConfigurationName WebMaintenance `
  -Credential (Get-Credential 'HK\alice.ops')

# 端点内验证(能做、不能做,都要试)
Get-Command                       # 只能看到暴露的命令
Restart-WebAppPool -Name ApiPool  # ✅ 允许
Restart-Computer                  # ❌ 被拒绝(命令不可见)
Stop-Process -Name w3wp           # ❌ 被拒绝(命令不可见)

再做两个端点:只读 SQL 与系统巡检(要点)

1) SQL 只读(SqlReadOnly.psrc / SqlReadOnly.pssc)

服务器上安装 SqlServer PowerShell 模块(或用 Invoke-Sqlcmd 的所在模块)。

VisibleCmdlets 限定在查询、状态类,禁止任何 DDL/DML。

RunAs 选择:

若仅本机+命名管道:仍可用虚拟账户,给其在实例内映射只读登录。

若需访问域内远端 SQL:不要用虚拟账户,改建服务账户端点(RunAs 用受控域账号;或专门做 gMSA 方案,超出本文展开)。

示例(片段):

# Role Capability 片段
-VisibleCmdlets @(
  @{ Name='Invoke-Sqlcmd';
     Parameters = @{
       Name='ServerInstance'; ValidateSet=@('HKSQL01','HKSQL02\REPORT');
       Name='Database';       ValidateSet=@('ReportDB','BillingRO')
     } },
  'Get-Service'  # 仅查看 SQL 服务
)

2) 系统巡检(SysInspector.psrc / SysInspector.pssc)

暴露 Get-WinEvent、Get-Process、Get-NetAdapter、Get-Volume、Get-CimInstance -ClassName Win32_* 等。

只读为原则,不开放任何写入类命令。

适合一线 NOC/外包同事排障时使用。

日常操作脚本(给一线同事的“傻瓜按钮”)

在跳板机上,我配了一些客户端函数,自动连到对应端点,把复杂参数藏起来:

function Restart-ApiPoolHK {
  param([string]$Server)
  Enter-PSSession -ComputerName $Server -ConfigurationName WebMaintenance `
    -Credential $global:OpsCred -Authentication Negotiate -UseSSL
  Restart-WebAppPool -Name ApiPool
  Exit-PSSession
}

function Tail-IISLogHK {
  param([string]$Server)
  Enter-PSSession -ComputerName $Server -ConfigurationName SysInspect `
    -Credential $global:OpsCred -Authentication Negotiate -UseSSL
  Get-ChildItem 'C:\inetpub\logs\LogFiles\W3SVC1' | Sort-Object LastWriteTime -Desc | Select-Object -First 1 |
    ForEach-Object { Get-Content $_.FullName -Tail 100 }
  Exit-PSSession
}

经验:把“进入端点—执行—退出”封装成函数,降低一线同事误用的概率。

审计与回溯(出事了,怎么还原真相)

Transcript:C:\JEA\Transcripts\ 下每次会话都有时间戳/用户/端点名。

事件日志:Microsoft-Windows-PowerShell/Operational、Security(4624/4634/4688 等)结合 SIEM。

变更挂钩:把关键命令(如 Restart-Service W3SVC)的 Transcript 片段推送到变更平台,做到“操作即记录”。

我踩过的坑(以及我是怎么填上的)

“我按文档放了 .psrc,端点里就是没生效?”

根因:RoleCapability 文件不在 …\Modules\<Module>\RoleCapabilities\ 或文件名与能力名不一致。

处理:检查路径与文件名;Get-Module -ListAvailable 看模块是否可见;必要时 Remove-Module 后重载。

WinRM HTTPS 连不上,证书报错

根因:CN/SAN 与主机名不匹配,或链路上被做了 SSL Inspection。

处理:用企业 CA 下发带 FQDN 的证书;跳板机信任该 CA;避免经过会做中间人代理的出口。

虚拟账户访问不了文件共享/SQL

说明:JEA 的虚拟账户是本地身份,默认没有域资源访问权。

处理:把需要跨资源的场景单独做服务账户端点(Register-PSSessionConfiguration -RunAsCredential 指定受控域账号),或改业务流程为“目标机推送结果”,避免端点去“拉”。

PowerShell 7 客户端偶发兼容性问题

情况:极少数老模块(如早期 WebAdministration)在 PowerShell 7 上调用 WSMan 端点有边角问题。

处理:服务端坚持 5.1,客户端若出兼容告警,改用 5.1 连接该端点;或在 7.x 中 pwsh -WorkingDirectory . -NoLogo 明确 -UseWindowsPowerShell(Windows 兼容层)。

参数白名单没写好,被用户“聪明地”绕过

例:你只白名单了 -Name,却忘了限制 -DisplayName 或 -InputObject。

处理:对同义/等效参数也设限制;必要时用包装函数把危险参数屏蔽掉,再把包装函数暴露到 RoleCapability。

变更与回滚(稳字当头)

更新端点:改 .psrc/.pssc 后,Register-PSSessionConfiguration -Name <Name> -Path <pssc> -Force。

临时下线:Disable-PSSessionConfiguration -Name <Name>(不断开已有会话,新会话被阻止)。

彻底撤销:Unregister-PSSessionConfiguration -Name <Name>;删除模块目录即可回到“未部署 JEA”状态。

应急后门:保留一条仅域管可用的 DefaultShell 端点(或物理带外/IPMI + 本地管),防止“误配置把自己锁外面”。

成效与对比(上线 3 周的度量)

指标 上线前(域管/RDP) 上线后(JEA 端点)
越权变更/周 2–3 起 0 起
故障恢复中位时长 18 分钟 9 分钟
审计取证时间 2 小时 10 分钟
域管密码使用频率 极低(仅变更窗口)

心得:速度并没有因为“受限”而变慢,反而因为“入口标准化 + 脚本化 + 审计可见”整体效率更高。

附:一个“服务账户端点”的简化示例(跨资源场景)

当确实需要访问远端 SQL/文件共享时,为该目的单独做端点与域服务账户(权限最小化、仅该端点可用):

$pssc = 'C:\JEA\SqlRO-Svc.pssc'
New-PSSessionConfigurationFile -Path $pssc `
  -SessionType RestrictedRemoteServer -LanguageMode NoLanguage `
  -TranscriptDirectory 'C:\JEA\Transcripts' `
  -ModulesToImport @('SqlServer') `
  -RoleDefinitions @{ 'HK\DB-RO' = @{ RoleCapabilities = 'SQLReadOnly' } }

# 用受控域账号注册(账号权限仅授予所需数据库的只读)
$cred = Get-Credential 'HK\svc_jea_sqlro'
Register-PSSessionConfiguration -Name 'SqlReadOnly' -Path $pssc -RunAsCredential $cred -Force

注意:服务账户的生命周期与凭据轮换要纳入 PAM/密管系统;这个端点只开放给需要的人,且只暴露只读命令。

结尾:一次“可预期的安全事故”

三周后,一个周五晚上的小演练,我们让新人在 WebMaintenance 端点里“尝试优化”IIS。他先后敲了 Restart-WebAppPool -Name PortalPool(成功)、Stop-Process w3wp(失败)、Remove-Item C:\inetpub\wwwroot -Recurse(失败)。我看着 SIEM 大屏上跳出的三条审计记录,忽然松了口气。JEA 没把人变聪明,但它逼迫我们把“能做什么、不能做什么”说清楚,并锁进系统里。
夜已深,机房的风还是冷,但我知道,即使半夜再来一通告警,大家也不会再为了“快”去借一把能开所有门的钥匙。

参考清单(你可以直接复用/改名)

模块路径:C:\Program Files\WindowsPowerShell\Modules\HKG.JEA.WebOps\1.0.0\

  • HKG.JEA.WebOps.psd1(模块清单)
  • RoleCapabilities\IISMaintainer.psrc
  • RoleCapabilities\SQLReadOnly.psrc
  • RoleCapabilities\SysInspector.psrc

端点配置:

  • C:\JEA\WebMaintenance.pssc
  • C:\JEA\SqlRO-Svc.pssc(如需)
  • C:\JEA\Transcripts\(会话转录输出)

如果你准备在大量服务器上推广,建议把 .psrc/.pssc 做成一个内部模块,用 CI/CD(Argo/Jenkins/Az DevOps)按 OU/标签分发,端点注册脚本按批次执行;再配合 GPO 强制日志策略,形成“配置即合规”。

——以上,就是我在香港服务器里落地 JEA 的全部过程和坑点。希望你看完,既能今天就照抄上线,也能在你们的场景里,拼出属于自己的“刚刚好权限”

目录结构
全文