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

香港服务器运行 Windows Server 时,如何启用 Just-in-Time(JIT)管理,降低管理员账号被滥用风险?

发布人:Minchunlin 发布时间:2025-08-25 10:17 阅读量:680


那是一个闷热的周三夜里,香港葵涌机房的湿度报警刚刚消下去,短信又跳了出来:关键业务 VM 有可疑 RDP 探测。我提起随身包进机房,风扇在机柜间低鸣,值守同事递来一杯冰水。我没开 3389 的公网入站,但外层设备上刚做过临时联调,窗口忘关就会出事。那晚之后,我把“JIT 管理”彻底落进所有 Windows Server 的基线,目标只有一个:需要时临时开、超时自动收,最小权限可审计。这篇就是我当时的落地方案与坑点记录。

我们的现场与约束

维度 参数/现状
机房与区域 香港(HK)、两套机柜,双上联(IX + 运营商)
服务器形态 物理 + 虚拟混合:Dell R650(2×Xeon Silver 4310、256GB RAM、2×1.92TB NVMe)+ VMware ESXi 7.0;部分是独立香港服务器(单租户)
OS 版本 Windows Server 2019/2022 Standard(Datacenter 少量)
管理入口 跳板机(Bastion,域内),默认禁公网;生产节点默认禁 3389 入站
账号策略 域账号 + LAPS(Windows LAPS,集成 Entra ID/AD),禁共享本地 admin 密码
审计 Windows 事件日志 + Sysmon,汇总到 SIEM(Elastic)
典型风险 临时开口子后忘关;管理员长期驻留本地 Administrators;脚本留后门;审批不到位

核心目标:在网络、身份、命令面三层实现 JIT:

  • 网络 JIT:RDP/WinRM 端口只在审批通过的时段、对特定源 IP 开放,超时自动收口。
  • 身份 JIT:管理员权限按需临时授予(TTL),到点自动回收(移出本地 Administrators 或失效临时账号/口令)。
  • 命令面 JIT(JEA):不需要“全能管理员”的操作,走 Just Enough Administration 限制命令集。

你不一定有 Azure 或固定厂商设备。本文方案:有云走云(PIM/JIT 互锁)、没云走原生(Windows 防火墙 + 计划任务 + JEA),都能落地。

总体流程(一图胜千言)

运维发起“临时维护”工单(期望时长、目标主机、源 IP)。

审批通过 → 自动化脚本在目标主机执行:

  • 新建仅对申请人 源 IP 的临时 RDP 放行(TTL=60 分钟可配)。
  • 将申请人域账号临时加入本地 Administrators(TTL 同步)。
  • 创建到点清理的计划任务:删除防火墙规则 + 移出管理员组。

若操作只需有限命令(如重启服务/读日志),则通过 JEA 端点,无需提权至本地 admin。

全程写日志、事件落盘,推送到 SIEM;异常可一键强制回收。

基线收紧(落地前的必做)

1. 关公网 3389(前置防火墙/云安全组 + 本机防火墙双保险)

2. 强制 NLA、禁弱加密;RDP 仅在跳板机侧开放。

3. 启用 Windows LAPS(新版 Windows LAPS,支持 AD/Entra),禁共享本地 admin 口令。

4. 开启审计:

  • 本地组成员变更(Event 4732/4733)、特权登录(4672)、登录成功(4624)
  • 防火墙规则变更(4946/4947)
  • PowerShell 脚本与模块日志(Module + Script Block + Transcription)

这一步是“地基”。没有它,JIT 只是“把门拉上但没插闩”。

方案一:网络 JIT——RDP/WinRM 只对指定源 IP、指定时间开放

1)最小可用脚本(目标主机上执行/远程推送执行)

# JIT-RDP.ps1
param(
  [Parameter(Mandatory=$true)][string]$SourceIP,
  [int]$DurationMinutes = 60,
  [string]$Tag = "$(Get-Date -Format 'yyyyMMddHHmmss')"
)

$ruleName = "JIT-RDP-$Tag"
Write-Host "Creating temporary firewall allow rule: $ruleName for $SourceIP"

# 1. 开口子(仅入站、仅 TCP 3389、仅指定源 IP)
New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol TCP -LocalPort 3389 -RemoteAddress $SourceIP | Out-Null

# 2. 到点自动清理(计划任务)
$removeScript = "Remove-NetFirewallRule -DisplayName `"$ruleName`""
$time = (Get-Date).AddMinutes($DurationMinutes)

$action  = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument "-NoProfile -WindowStyle Hidden -Command `$ErrorActionPreference='Stop'; $removeScript"
$trigger = New-ScheduledTaskTrigger -Once -At $time
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName $ruleName -Action $action -Trigger $trigger -Principal $principal | Out-Null

# 3. 审计落盘
$log = "C:\ProgramData\JIT\JIT-RDP.log"
New-Item -ItemType Directory -Force (Split-Path $log) | Out-Null
"[$(Get-Date -Format 'u')] ADD $ruleName Source=$SourceIP TTL=$DurationMinutes" | Out-File -FilePath $log -Append -Encoding utf8

Write-Host "Done. Rule will be removed at $time"

实战建议:

  • 只允许单个或极窄网段源地址;禁止 Any。
  • 跳板机到业务机的 RDP/WinRM 再做一层名单制(仅域运维组)。
  • 如果你使用厂商防火墙(如 FortiGate),用其 API 做同样的“临时策略 + schedule”,和本机防火墙双收口。

方案二:身份 JIT——管理员权限临时授予、自动回收

2.1 域账号临时加入本地 Administrators(计划任务到点回收)

# JIT-Admin.ps1
param(
  [Parameter(Mandatory=$true)][string]$SamAccount,   # 如 corp\alice
  [int]$DurationMinutes = 60,
  [string]$Tag = "$(Get-Date -Format 'yyyyMMddHHmmss')"
)

$taskName = "JIT-ADMIN-$Tag"

# 1. 加入本地 Administrators
net localgroup Administrators "$SamAccount" /add

# 2. 回收计划任务
$removeCmd = "net localgroup Administrators `"$SamAccount`" /delete"
$time = (Get-Date).AddMinutes($DurationMinutes)
$action  = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument "/c $removeCmd"
$trigger = New-ScheduledTaskTrigger -Once -At $time
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal | Out-Null

# 3. 记录
$log = "C:\ProgramData\JIT\JIT-Admin.log"
New-Item -ItemType Directory -Force (Split-Path $log) | Out-Null
"[$(Get-Date -Format 'u')] ADD $SamAccount TTL=$DurationMinutes task=$taskName" | Out-File -FilePath $log -Append -Encoding utf8

Write-Host "Granted temporary admin to $SamAccount. Will be revoked at $time"

配套 GPO:禁用“将域用户自动添加到本地管理员”的旧策略;只通过上述 JIT 脚本临时添加。

审计点:4732/4733(本地组变更)、4688(进程创建,记录脚本执行)、PowerShell 脚本块日志。

2.2 Windows LAPS(新)+ 时间敏感取用(可选)

场景:不想把域账号加进本地 Administrators,但又需要短时做“本地 admin”。

做法:启用 Windows LAPS(组策略/Entra),将本地管理员口令随取随用,并配置短 TTL 和 强制轮换。

取口令完成后立刻触发 RotateNow;或者把 JIT 窗口结束时执行强制轮换,避免密码残留。

注意旧版 LAPS(MSI)与新版 Windows LAPS配置差异较大,域/Entra 环境要统一标准,避免两套并存。

方案三:命令面 JIT(JEA)——只给“刚好够用”的命令

很多现场根本不需要“管理员桌面”。比如应用卡死,只需要 Restart-Service 到指定服务。用 JEA 定义角色能力和受限会话,用户连上来只能执行白名单命令。

3.1 Role Capability(例:允许查看/重启特定服务)

C:\Program Files\WindowsPowerShell\Modules\Company.JEA\RoleCapabilities\ServiceOps.psrc

@{
    GUID = '0b6f7b2e-1c2a-4c42-b1e0-aaaa1111bbbb'
    Author = 'Ops'
    VisibleCmdlets = @(
        @{ Name = 'Get-Service'; Parameters = @{ Name='Name'; ValidateSet=@('IISADMIN','W3SVC','Spooler') } },
        @{ Name = 'Restart-Service'; Parameters = @{ Name='Name'; ValidateSet=@('W3SVC') } }
    )
    VisibleProviders = @()
    ScriptsToProcess = @()
    ModulesToImport = @()
}

3.2 会话配置

C:\Program Files\WindowsPowerShell\Modules\Company.JEA\JEASvcOps.pssc

@{
    SessionType = 'RestrictedRemoteServer'
    TranscriptDirectory = 'C:\ProgramData\JEA\Transcripts'
    RunAsVirtualAccount = $true
    RoleDefinitions = @{
        'CORP\ServiceOpsGroup' = @{ RoleCapabilities = 'ServiceOps' }
    }
}

注册并启用:

Register-PSSessionConfiguration -Name 'JEA-ServiceOps' -Path 'C:\Program Files\WindowsPowerShell\Modules\Company.JEA\JEASvcOps.pssc' -Force
Restart-Service WinRM

这样绝大部分“看与重启”类操作都不需要“提管理员”。JIT 窗口只在极少数场景打开,风险面小得多。

把三层串起来:一键启用/回收(示例编排脚本)

# Invoke-JIT.ps1 —— 由跳板机执行,通过 WinRM 到目标主机
param(
  [Parameter(Mandatory=$true)][string]$Target,       # 目标主机 FQDN
  [Parameter(Mandatory=$true)][string]$Operator,     # 申请人,如 corp\alice
  [Parameter(Mandatory=$true)][string]$SourceIP,     # 申请人源 IP
  [int]$Minutes = 60
)

$session = New-PSSession -ComputerName $Target -Authentication Negotiate

Invoke-Command -Session $session -ScriptBlock {
    param($Operator,$SourceIP,$Minutes)
    & "C:\Ops\JIT-RDP.ps1" -SourceIP $SourceIP -DurationMinutes $Minutes
    & "C:\Ops\JIT-Admin.ps1" -SamAccount $Operator -DurationMinutes $Minutes
} -ArgumentList $Operator,$SourceIP,$Minutes

Write-Host "[$Target] JIT window granted to $Operator from $SourceIP for $Minutes minutes."

在我们环境里,这个脚本由审批流(Power Automate/ServiceNow)触发,变量从工单里带入。审批通过才会远程到目标机执行。

验收指标(实施前后对比)

指标 改造前 改造后(目标)
3389 暴露时长/周 120 分钟 ≤ 10 分钟
临时管理员留存事件/月 3–5 起 0(全部自动回收)
审批平均耗时 15–20 分钟 5–8 分钟(半自动)
未授权登录告警/月 偶发 显著下降
审计可追溯性 低(口头记录) 高(日志 + 工单 + 事件)

常见坑与现场解法

计划任务不执行/不清理

原因:未以 SYSTEM/最高权限运行;机器时钟漂移;脚本路径有空格未转义。

解法:用 -UserId SYSTEM -RunLevel Highest;统一 NTP;所有命令转义并走 cmd /c 或 PowerShell -Command。

源 IP 变化导致连不上

让申请人先通过跳板机(固定出口),或工单要求固定出口 IP。

避免对公网动态宽带直开口子。

Windows LAPS 与旧版 LAPS 混用

统一迁移到 Windows LAPS;梳理 GPO,确认只生效一套;完成后强制轮换一次。

JEA 被“聪明人”绕过

RoleCapability 严格限制 VisibleCmdlets 和参数;禁用 Invoke-Expression、Add-Type 等;记录 Transcript,异常上 SIEM 告警。

运维觉得麻烦

让脚本“一键开窗”,自动填工单字段;默认 30–60 分钟 TTL,超时必须重申。

统计每周开窗次数,结合 SRE 回顾减少不必要的人工接入。

防火墙规则名冲突

规则命名带时间戳/Guid;清理任务按DisplayName 精确匹配。

服务器重启期间

清理任务用 Absolute Time 触发,重启后依然有效;同时在启动脚本里加“兜底清理”(发现过期 JIT 规则即删)。

最后,把审计做满

事件订阅:

  • 4732/4733(本地组变更)
  • 4946/4947(防火墙规则变更)
  • 4624/4625(登录)+ 4672(特权)
  • PowerShell Script Block 与 Transcript

落 SIEM 后做三类告警:

  • JIT 窗口外的 3389 入站尝试;
  • 本地 Administrators 里出现非 JIT 任务添加的成员;
  • 口令未轮换/计划任务失败。

附:最小可用清单(可直接抄)

C:\Ops\JIT-RDP.ps1(上文脚本)

C:\Ops\JIT-Admin.ps1(上文脚本)

Company.JEA 模块:RoleCapabilities\ServiceOps.psrc + JEASvcOps.pssc

GPO:

  • 启用 Windows LAPS,配置 TTL 与强制轮换
  • 开启 PowerShell 日志(Script Block、Module、Transcription)
  • 审计策略:账户管理、特权使用、对象访问
  • SIEM 解析与告警三条规则

结尾:又一次“深夜开窗”

两个月后的一次版本回退,应用只需重启服务。年轻同事在工单里点了“JEA-ServiceOps”,我手机“叮”一声,审批通过。没有大开管理员、没有放行公网 RDP,他在跳板机上跑了两条命令,窗口 30 分钟后自己关上。机房里的风还是从走廊尽头吹来,但我知道,门是开的、也是会自己关上的。这就是 JIT 在我环境里的意义——不牺牲效率的前提下,把风险留在可控的时间盒里。

目录结构
全文