JEA 角色能力

创建 JEA 终结点时,需要定义一个或多个角色功能,它们描述了用户可在 JEA 会话中执行哪些操作。 角色功能是一个具有 .psrc 扩展名的 PowerShell 数据文件,它列出了所有向连接用户开放的 cmdlet、函数、提供程序和外部程序。

确定要允许哪些命令

创建角色功能文件的第一步是考虑用户需要访问的内容。 要求收集过程可能需要一段时间,但这一点很重要。 让用户访问的 cmdlet 和函数太少可能会阻止他们完成工作。 允许用户访问过多的 cmdlet 和函数可能会使他们执行超出预期的操作,从而削弱你的安全防御。

此过程的方式取决于你的组织和目标。 以下提示有助于确保你位于正确的路径上。

  1. 确定 用户用来完成任务的命令。 这可能涉及调查 IT 人员、检查自动化脚本或分析 PowerShell 会话脚本和日志。
  2. 尽可能将命令行工具的使用替换为 PowerShell 等效项,以获得最佳的审核和 JEA 定制体验。 外部程序不能像 JEA 中的本机 PowerShell cmdlet 和函数那样精细地受到约束。
  3. cmdlet 的范围限制为仅允许特定参数或参数值。 如果用户应仅管理系统的一部分,这一点尤其重要。
  4. 创建自定义 函数以替换在 JEA 中难以约束的复杂命令或命令。 包装复杂命令或应用其他验证逻辑的简单函数可以为管理员和最终用户提供额外的控制。
  5. 测试 用户或自动化服务使用的特定范围内允许的命令列表,并根据需要进行调整。

潜在危险命令的示例

仔细选择命令对于确保 JEA 终结点不允许用户提升其权限非常重要。

重要

JEA 会话中,用户成功所需的基本信息以及命令通常使用提升的权限运行。

以下列表包含一些命令示例,如果允许处于不受约束状态,则可以恶意使用这些命令。 这不是详尽的列表,只应作为起点以供参考。

  • 风险: 授予连接用户管理员权限以绕过 JEA

    示例:

    Add-LocalGroupMember -Member 'CONTOSO\jdoe' -Group 'Administrators'
    

    相关命令:

    • Add-ADGroupMember
    • Add-LocalGroupMember
    • net.exe
    • dsadd.exe
  • 风险: 运行任意代码,例如恶意软件、攻击或自定义脚本以绕过保护

    示例:

    Start-Process -FilePath '\\san\share\malware.exe'
    

    相关命令:

    • Start-Process
    • New-Service
    • Invoke-Item
    • Invoke-WmiMethod
    • Invoke-CimMethod
    • Invoke-Expression
    • Invoke-Command
    • New-ScheduledTask
    • Register-ScheduledJob

创建角色功能文件

可以使用 New-PSRoleCapabilityFile cmdlet 创建新的 PowerShell 角色功能文件。

New-PSRoleCapabilityFile -Path .\MyFirstJEARole.psrc

应编辑所创建的角色功能文件,确保仅允许该角色所需的命令。 PowerShell 帮助文档包括文件配置方式的多个示例。

允许 PowerShell 命令行工具和函数

若要授权用户运行 PowerShell cmdlet 或函数,请将 cmdlet 或函数名称添加到 VisibleCmdlet 或VisibleFunctions 字段。 如果不确定命令是 cmdlet 还是函数,则可以在输出中运行 Get-Command <name> 并检查 CommandType 属性。

VisibleCmdlets = @('Restart-Computer', 'Get-NetIPAddress')

有时,特定 cmdlet 或函数的范围过于广泛,无法满足用户的需求。 例如,DNS 管理员可能需要访问才能重启 DNS 服务。 在多租户环境中,租户有权访问自助服务管理工具。 租户应仅限于管理自己的资源。 对于这些情况,可以限制从 cmdlet 或函数公开哪些参数。

VisibleCmdlets = @{
    Name       = 'Restart-Computer'
    Parameters = @{ Name = 'Name' }
}

在更高级的方案中,可能还需要限制用户可能使用这些参数的值。 通过角色功能可以定义一组值或一个正则表达式模式,用于确定允许哪些输入。

VisibleCmdlets = @(
    @{
        Name       = 'Restart-Service'
        Parameters = @{ Name = 'Name'; ValidateSet = @('Dns', 'Spooler') }
    }
    @{
        Name       = 'Start-Website'
        Parameters = @{ Name = 'Name'; ValidatePattern = 'HR_*' }
    }
)

注释

即使限制可用参数,也始终允许 使用通用 PowerShell 参数 。 不应在“参数”字段中显式列出它们。

以下列表介绍了自定义可见 cmdlet 或函数的各种方法。 可以在 VisibleCmdlet 字段中混合和匹配以下任一项。

  • 用例: 允许用户在不限制参数的情况下运行 My-Func

    @{ Name = 'My-Func' }
    
  • 用例:允许用户从模块 MyModule 运行My-Func,而无需对参数有任何限制。

    @{ Name = 'MyModule\My-Func' }
    
  • 用例: 允许用户使用谓词 My运行任何 cmdlet 或函数。

    @{ Name = 'My-*' }
    
  • 用例: 允许用户使用名词 Func运行任何 cmdlet 或函数。

    @{ Name = '*-Func' }
    
  • 用例:允许用户使用Param1Param2参数运行My-Func。 任何值都可以提供给参数。

    @{ Name = 'My-Func'; Parameters = @{ Name = 'Param1'}, @{ Name = 'Param2' }}
    
  • 用例:允许用户使用Param1参数运行My-Func。 参数只能接收 Value1Value2

    @{
        Name       = 'My-Func'
        Parameters = @{ Name = 'Param1'; ValidateSet = @('Value1', 'Value2') }
    }
    
  • 用例:允许用户使用Param1参数运行My-Func。 可以向参数提供任何以开头 contoso 的值。

    @{
        Name       = 'My-Func'
        Parameters = @{ Name = 'Param1'; ValidatePattern = 'contoso.*' }
    }
    

警告

为了获得最佳安全做法,建议在定义可见 cmdlet 或函数时不要使用通配符。 相反,应显式列出每个受信任的命令,以确保没有其他共享同一命名方案的命令无意中获得授权。

不能同时将 ValidatePatternValidateSet 应用于同一 cmdlet 或函数。

如果这样做, ValidatePattern 将替代 ValidateSet

有关 ValidatePattern 的详细信息,请查看 Hey, Scripting Guy! 文章PowerShell 正则表达式 参考内容。

允许外部命令和 PowerShell 脚本

若要允许用户在 JEA 会话中运行可执行文件和 PowerShell 脚本(.ps1),必须将完整路径添加到 VisibleExternalCommands 字段中的每个程序。

VisibleExternalCommands = @(
    'C:\Windows\System32\whoami.exe'
    'C:\Program Files\Contoso\Scripts\UpdateITSoftware.ps1'
)

如果可能,请对授权的任何外部可执行文件使用 PowerShell cmdlet 或函数等效项,因为你可以控制 PowerShell cmdlet 和函数允许的参数。

许多可执行文件允许你读取当前状态,然后通过提供不同的参数对其进行更改。

例如,请考虑管理系统上托管的网络共享的文件服务器管理员的角色。 管理共享的一种方法是使用 net share。 但是,允许 net.exe 是危险的,因为用户可以使用命令 net group Administrators unprivilegedjeauser /add 来获得管理员权限。 更安全的选项是允许 Get-SmbShare cmdlet,该 cmdlet 可实现相同的结果,但范围要有限得多。

在 JEA 会话中向用户提供外部命令时,请始终指定可执行文件的完整路径。 这可以防止执行位于系统上其他位置的类似命名和潜在恶意程序。

允许访问 PowerShell 提供程序

默认情况下,JEA 会话中不提供 PowerShell 提供程序。 这可降低向连接用户披露敏感信息和配置设置的风险。

当需要时,您可以使用 VisibleProviders 命令来允许访问 PowerShell 提供程序。 要获取提供商的完整列表,请运行 Get-PSProvider

VisibleProviders = 'Registry'

对于需要访问文件系统、注册表、证书存储或其他敏感提供程序的简单任务,请考虑编写代表用户处理提供程序的自定义函数。 JEA 会话中提供的函数、cmdlet 和外部程序不受与 JEA 相同的约束。 默认情况下,他们可以访问任何提供程序。 还需要考虑在用户需要将文件复制到 JEA 终结点或从 JEA 终结点复制文件时使用用户 驱动器

创建自定义函数

可以在角色功能文件中创作自定义函数,以简化最终用户的复杂任务。 当需要 cmdlet 参数值的高级验证逻辑时,自定义函数也很有用。 可以在 FunctionDefinitions 字段中编写简单的函数:

VisibleFunctions = 'Get-TopProcess'

FunctionDefinitions = @{
    Name        = 'Get-TopProcess'
    ScriptBlock = {
        param($Count = 10)

        Get-Process |
            Sort-Object -Property CPU -Descending |
            Microsoft.PowerShell.Utility\Select-Object -First $Count
    }
}

重要

不要忘记将自定义函数的名称添加到 VisibleFunctions 字段,以便 JEA 用户可以运行它们。

自定义函数的正文(脚本块)在系统的默认语言模式下运行,不受 JEA 的语言约束的约束。 这意味着函数可以访问文件系统和注册表,并运行在角色功能文件中不可见的命令。 请注意,在使用参数时避免运行任意代码。 避免将用户的输入直接传递到 cmdlet 中,例如 Invoke-Expression

在上面的示例中,请注意使用了完全限定的模块名称(FQMN), Microsoft.PowerShell.Utility\Select-Object 而不是简写 Select-Object。 角色功能文件中定义的函数仍受 JEA 会话范围的约束,其中包括 JEA 创建的代理函数 JEA 以约束现有命令。

默认情况下, Select-Object 所有 JEA 会话中都是一个受约束的 cmdlet,不允许在对象上选择任意属性。 若要在函数中使用不受约束 Select-Object ,必须使用 FQMN 显式请求完整实现。 在 JEA 会话中,从函数调用的任何受约束的 cmdlet 仍保持相同的约束。 有关详细信息,请参阅 about_Command_Precedence

如果要编写多个自定义函数,则将其放在 PowerShell 脚本模块中更为方便。 使用 VisibleFunctions 字段在 JEA 会话中显示这些函数,就像使用内置和第三方模块一样。

若要使 Tab 完成在 JEA 会话中正常工作,必须在 VisibleFunctions 列表中包括内置函数TabExpansion2

使角色功能可用于配置

在 PowerShell 6 之前,若要查找角色功能文件,PowerShell 必须将其存储在 RoleCapabilities PowerShell 模块中的文件夹中。 该模块可以存储在环境变量中包含的 $Env:PSModulePath 任何文件夹中,但不应将其 $Env:SystemRoot\System32 放入或不受信任的用户可以修改文件的文件夹。

以下示例在托管角色功能文件的路径中创建$Env:ProgramFiles名为 ContosoJEA 的 PowerShell 脚本模块。

# Create a folder for the module
$modulePath = Join-Path $Env:ProgramFiles "WindowsPowerShell\Modules\ContosoJEA"
New-Item -ItemType Directory -Path $modulePath

# Create an empty script module and module manifest.
# At least one file in the module folder must have the same name as the folder itself.
$rootModulePath = Join-Path $modulePath "ContosoJEAFunctions.psm1"
$moduleManifestPath = Join-Path $modulePath "ContosoJEA.psd1"
New-Item -ItemType File -Path $RootModulePath
New-ModuleManifest -Path $moduleManifestPath -RootModule "ContosoJEAFunctions.psm1"

# Create the RoleCapabilities folder and copy in the PSRC file
$rcFolder = Join-Path $modulePath "RoleCapabilities"
New-Item -ItemType Directory $rcFolder
Copy-Item -Path .\MyFirstJEARole.psrc -Destination $rcFolder

有关 PowerShell 模块的详细信息,请参阅 了解 PowerShell 模块

从 PowerShell 6 开始, RoleDefinitions 属性已添加到会话配置文件。 此属性允许为角色定义指定角色配置文件的位置。 请参阅 New-PSSessionConfigurationFile 中的示例。

更新角色功能

你可以编辑角色功能文件以随时更新设置。 更新角色功能后启动的任何新的 JEA 会话都将反映修订后的功能。

这就是为什么控制对角色功能文件夹的访问非常重要的原因。 仅允许高度信任的管理员更改角色功能文件。 如果不受信任的用户可以更改角色功能文件,他们可以轻松授予自己对 cmdlet 的访问权限,这些 cmdlet 允许他们提升其特权。

对于希望锁定对角色功能的访问的管理员,请确保本地系统对角色功能文件和包含模块具有只读访问权限。

如何合并角色功能

当用户输入 JEA 会话时,用户有权访问 会话配置文件 中的所有匹配角色功能。 JEA 会尝试为用户提供任何角色允许的最宽松的命令集。

VisibleCmdlets 和 VisibleFunctions

最复杂的合并逻辑会影响 cmdlet 和函数,这些 cmdlet 和函数可以在 JEA 中限制其参数和参数值。

规则如下:

  1. 如果 cmdlet 仅在一个角色中可见,则具有任何适用参数约束的用户可以看到该 cmdlet。
  2. 如果 cmdlet 在多个角色中可见,并且每个角色对 cmdlet 具有相同的约束,则 cmdlet 对具有这些约束的用户可见。
  3. 如果 cmdlet 在多个角色中可见,并且每个角色允许一组不同的参数,则 cmdlet 和跨每个角色定义的所有参数对用户可见。 如果一个角色对参数没有约束,则允许所有参数。
  4. 如果一个角色为 cmdlet 参数定义了验证集或验证模式,而另一个角色允许该参数,但不限制参数值,则忽略验证集或模式。
  5. 如果在多个角色中为同一 cmdlet 参数定义了验证集,则允许来自所有验证集的所有值。
  6. 如果在多个角色中为同一 cmdlet 参数定义了验证模式,则允许与任何模式匹配的任何值。
  7. 如果验证集在一个或多个角色中定义,并且验证模式在另一个角色中为同一 cmdlet 参数定义,则忽略验证集,规则 (6) 适用于其余验证模式。

下面是根据以下规则合并角色的示例:

# Role A Visible Cmdlets
$roleA = @{
    VisibleCmdlets = @(
        'Get-Service'
         @{
            Name       = 'Restart-Service'
            Parameters = @{ Name = 'DisplayName'; ValidateSet = 'DNS Client' }
        }
    )
}

# Role B Visible Cmdlets
$roleB = @{
    VisibleCmdlets = @(
        @{
            Name       = 'Get-Service';
            Parameters = @{ Name = 'DisplayName'; ValidatePattern = 'DNS.*' }
        }
        @{
            Name       = 'Restart-Service'
            Parameters = @{ Name = 'DisplayName'; ValidateSet = 'DNS Server' }
        }
    )
}

# Resulting permissions for a user who belongs to both role A and B
# - The constraint in role B for the DisplayName parameter on Get-Service
#   is ignored because of rule #4
# - The ValidateSets for Restart-Service are merged because both roles use
#   ValidateSet on the same parameter per rule #5
$mergedAandB = @{
    VisibleCmdlets = @(
        'Get-Service'
        @{
            Name = 'Restart-Service';
            Parameters = @{
                Name = 'DisplayName'
                ValidateSet = 'DNS Client', 'DNS Server'
            }
        }
    )
}

VisibleExternalCommands、VisibleAliases、VisibleProviders、ScriptsToProcess

角色功能文件中的所有其他字段将添加到一组累积允许的外部命令、别名、提供程序和启动脚本中。 一个角色功能中提供的任何命令、别名、提供程序或脚本都可供 JEA 用户使用。

请注意,确保来自一个角色功能和另一个角色的 cmdlet/functions/command 的组合提供程序集不允许用户无意访问系统资源。 例如,如果一个角色允许 Remove-Item cmdlet,而另一个角色允许 FileSystem 提供程序,你就有可能让 JEA 用户在你的计算机上删除任意文件。 有关识别用户有效权限的其他信息,请参阅 审核 JEA 一文。

后续步骤

创建会话配置文件