为 Windows 容器创建 gMSA

基于 Windows 的网络通常使用 Active Directory (AD) 为用户、计算机和其他网络资源之间的身份验证和授权提供便利。 企业应用程序开发人员通常将其应用设计为在其中集成 AD,并在加入域的服务器上运行,以利用集成 Windows 身份验证,这使得用户和其他服务可以轻松地使用其标识自动透明地登录到应用程序。 本文介绍了如何开始将 Active Directory 组托管服务帐户用于 Windows 容器。

尽管 Windows 容器无法加入域,但它们仍可使用 Active Directory 域标识来支持各种身份验证方案。 若要实现此目的,你可以将 Windows 容器配置为通过组托管服务帐户 (gMSA) 来运行。此帐户是 Windows Server 2012 中引入的一种特殊类型的服务帐户,并旨在允许多台计算机共享某个标识而无需知道该标识的密码。 Windows 容器无法加入域,但许多 Windows 容器中运行的 Windows 应用程序仍需要 AD 身份验证。 若要使用 AD 身份验证,可以将 Windows 容器配置为使用组托管服务帐户 (gMSA) 运行。

最初引入用于 Windows 容器的 gMSA 时,它要求容器主机加入域,这使用户手动将 Windows 工作器节点加入域时产生了很多开销。 用于 Windows 容器的 gMSA 支持未加入域的容器主机后,此限制得以解决。 但仍支持使用已加入域的容器主机的原始功能。 使用未加入域的容器主机时,对 gMSA 的改进包括:

  • 不再要求将 Windows 工作器节点手动加入域中,这曾给用户带来很大开销。 对于缩放场景,这将简化该过程。
  • 在滚动更新场景中,用户不再需要将节点重新加入域。
  • 管理工作器节点计算机帐户来检索 gMSA 服务帐户密码的过程更加简单。
  • 使用 Kubernetes 配置 gMSA 的端到端过程的复杂程度降低。

备注

若要了解 Kubernetes 社区如何支持将 gMSA 与 Windows 容器配合使用,请参阅配置 gMSA

gMSA 体系结构和改进

为解决用于 Windows 容器的 gMSA 初始实现的限制,新 gMSA 支持未加入域的容器主机使用可移植用户标识(而不是主机标识)来检索 gMSA 凭据。 因此,不再需要将 Windows 工作器节点加入到域中,但这仍受支持。 用户标识/凭据存储在容器主机可访问的机密存储区中(例如作为 Kubernetes 机密),只允许经过身份验证的用户进行检索。

组托管服务帐户版本 2 关系图

gMSA 对未加入域的容器主机的支持提供了一种灵活性,因而可在不将主机节点加入域的情况下使用 gMSA 创建容器。 从 Windows Server 2019 开始,支持 ccg.exe,通过它可使用一种插件机制从 Active Directory 检索 gMSA 凭据。 可以使用该标识启动容器。 有关此插件的详细信息,请参阅 ICcgDomainAuthCredentials 接口

备注

在 Azure Stack HCI 上的 Azure Kubernetes 服务中,可以使用插件从 ccg.exe 与 AD 通信,然后检索 gMSA 凭据。 有关详细信息,请参阅在 Azure Stack HCI 上的 AKS 中配置组托管服务帐户

查看下图,按照 Container Credential Guard 过程的步骤操作:

  1. 使用 CredSpec 文件作为输入,ccg.exe 过程在节点主机上启动。

  2. ccg.exe 使用 CredSpec 文件中的信息启动插件,然后检索与插件关联的机密存储中的帐户凭据。

  3. ccg.exe 使用检索到的帐户凭据从 AD 检索 gMSA 密码。

  4. ccg.exe 使 gMSA 密码可用于已请求凭据的容器。

  5. 容器使用 gMSA 密码向域控制器进行身份验证,以获取 Kerberos 票证授予票证 (TGT)。

  6. 在容器中作为网络服务或本地系统运行的应用程序现可对域资源(例如 gMSA)进行身份验证和访问。

    ccg.exe 过程图示

必备条件

若要通过组托管服务帐户运行 Windows 容器,你需要具有以下项:

  • 一个 Active Directory 域,其中至少有一台运行 Windows Server 2012 或更高版本的域控制器。 若要使用 gMSA,在林或域级别没有功能要求,但 gMSA 密码只能由运行 Windows Server 2012 或更高版本的域控制器分发。 有关详细信息,请参阅 gMSA 的 Active Directory 要求
  • 创建 gMSA 帐户所需的权限。 若要创建 gMSA 帐户,你需要是域管理员,或者使用已被委派了“创建 msDS-GroupManagedServiceAccount 对象”权限的帐户。
  • 访问 Internet,下载 CredentialSpec PowerShell 模块。 如果在无连接的环境中工作,则可先在能够访问 Internet 的计算机上保存此模块,然后将其复制到开发计算机或容器主机。

一次性 Active Directory 准备工作

如果尚未在域中创建 gMSA,则需生成密钥分发服务 (KDS) 根密钥。 KDS 负责创建、轮换 gMSA 密码并将其发布到经授权的主机。 当容器主机需要使用 gMSA 来运行容器时,它将与 KDS 联系以检索当前密码。

若要检查是否已创建 KDS 根密钥,请在安装了 AD PowerShell 工具的域控制器或域成员上以域管理员身份运行以下 PowerShell cmdlet:

Get-KdsRootKey

如果该命令返回了密钥 ID,则一切都已设置完毕,可跳到创建组托管服务帐户部分。 否则,请继续创建 KDS 根密钥。

在包含多个域控制器的生产环境或测试环境中,以域管理员身份在 PowerShell 中运行以下 cmdlet,以创建 KDS 根密钥。

# For production environments
Add-KdsRootKey -EffectiveImmediately

尽管该命令暗示密钥会立即生效,但你需要等待 10 小时,KDS 根密钥才会被复制并可供在所有域控制器上使用。

如果域中只有一台域控制器,则可以通过将该密钥设置为在 10 小时前生效来加速此过程。

重要

请勿在生产环境中使用此方法。

# For single-DC test environments ONLY
Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10)

创建组托管服务帐户

使用集成 Windows 身份验证的每个容器都需要至少一个 gMSA。 当以“系统”或“网络服务”身份运行的应用访问网络上的资源时,将使用主要 gMSA。 gMSA 的名称将成为网络上的容器名称,不管分配给该容器的主机名是什么。 如果你希望以不同于容器计算机帐户的身份在容器中运行服务或应用程序,还可以为容器配置其他 gMSA。

创建 gMSA 时,还会创建一个可在许多不同计算机上同时使用的共享标识。 对 gMSA 密码的访问受 Active Directory 访问控制列表保护。 建议为每个 gMSA 帐户创建一个安全组,并将相关容器主机添加到该安全组,以限制对密码的访问。

最后,由于容器不会自动注册任何服务主体名称 (SPN),因此你需要为 gMSA 帐户手动创建至少一个主机 SPN。

通常,主机或 http SPN 使用与 gMSA 帐户相同的名称进行注册,但如果客户端从负载平衡器后面访问容器化应用程序,或者使用与 gMSA 名称不同的 DNS 名称,则你可能需要使用不同的服务名称。

例如,如果 gMSA 帐户命名为“WebApp01”,但你的用户访问 mysite.contoso.com 处的站点,则应在 gMSA 帐户上注册一个 http/mysite.contoso.com SPN。

某些应用程序可能需要为其独特的协议使用额外的 SPN。 例如,SQL Server 需要 MSSQLSvc/hostname SPN。

下表列出了创建 gMSA 所需的属性。

gMSA 属性 必需的值 示例
名称 任何有效的帐户名称。 WebApp01
DnsHostName 追加到帐户名称的域名。 WebApp01.contoso.com
ServicePrincipalNames 至少设置主机 SPN,并根据需要添加其他协议。 'host/WebApp01', 'host/WebApp01.contoso.com'
PrincipalsAllowedToRetrieveManagedPassword 包含你的容器主机的安全组。 WebApp01Hosts

确定 gMSA 的名称后,在 PowerShell 中运行以下 cmdlet 来创建安全组和 gMSA。

提示

你需要使用属于 域管理员 安全组的帐户或已被委派了“创建 msDS-GroupManagedServiceAccount 对象”权限的帐户来运行以下命令。 New-ADServiceAccount cmdlet 是远程服务器管理工具中的 AD PowerShell 工具的一部分。

建议分别为开发环境、测试环境和生产环境创建单独的 gMSA 帐户。

有关为已加入域的容器主机用例创建 gMSA 帐户的说明

# Replace 'WebApp01' and 'contoso.com' with your own gMSA and domain names, respectively.

# To install the AD module on Windows Server, run Install-WindowsFeature RSAT-AD-PowerShell
# To install the AD module on Windows 10 version 1809 or later, run Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
# To install the AD module on older versions of Windows 10, see https://aka.ms/rsat

# Create the security group
New-ADGroup -Name "WebApp01 Authorized Hosts" -SamAccountName "WebApp01Hosts" -GroupScope DomainLocal

# Create the gMSA
New-ADServiceAccount -Name "WebApp01" -DnsHostName "WebApp01.contoso.com" -ServicePrincipalNames "host/WebApp01", "host/WebApp01.contoso.com" -PrincipalsAllowedToRetrieveManagedPassword "WebApp01Hosts"

# Add your container hosts to the security group
Add-ADGroupMember -Identity "WebApp01Hosts" -Members "ContainerHost01$", "ContainerHost02$", "ContainerHost03$"

有关为未加入域的容器主机用例创建 gMSA 帐户的说明

对未加入域的容器主机使用 gMSA 时,请创建并添加标准用户帐户,而不是将容器主机添加到 WebApp01Hosts 安全组。

# Replace 'WebApp01' and 'contoso.com' with your own gMSA and domain names, respectively. 

# To install the AD module on Windows Server, run Install-WindowsFeature RSAT-AD-PowerShell
# To install the AD module on Windows 10 version 1809 or later, run Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
# To install the AD module on older versions of Windows 10, see https://aka.ms/rsat

# Create the security group
New-ADGroup -Name "WebApp01 Authorized Accounts" -SamAccountName "WebApp01Accounts" -GroupScope DomainLocal

# Create the gMSA
New-ADServiceAccount -Name "WebApp01" -DnsHostName "WebApp01.contoso.com" -ServicePrincipalNames "host/WebApp01", "host/WebApp01.contoso.com" -PrincipalsAllowedToRetrieveManagedPassword "WebApp01Accounts"

# Create the standard user account. This account information needs to be stored in a secret store and will be retrieved by the ccg.exe hosted plug-in to retrieve the gMSA password. Replace 'StandardUser01' and 'SecurePassword' with a unique username and password. We recommend using a random, long, machine-generated password.
New-ADUser -Name "StandardUser01" -AccountPassword "SecurePassword" -Enabled 1 

# Add your container hosts to the security group
Add-ADGroupMember -Identity "WebApp01Accounts" -Members "StandardUser01"

准备容器主机

为已加入域的容器主机用例准备容器主机

将通过 gMSA 运行 Windows 容器的每个容器主机都必须加入域,并且必须有权检索 gMSA 密码。

  1. 将你的计算机加入 Active Directory 域。

  2. 确保你的主机属于控制着 gMSA 密码访问的安全组。

  3. 重启计算机,使其获得新的组成员身份。

  4. 安装 Docker Desktop for Windows 10Docker for Windows Server

  5. (建议)运行 Test-ADServiceAccount 来验证主机是否可以使用 gMSA 帐户。 如果该命令返回了 False,请遵循 故障排除说明进行操作。

    # To install the AD module on Windows Server, run Install-WindowsFeature RSAT-AD-PowerShell
    # To install the AD module on Windows 10 version 1809 or later, run Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
    # To install the AD module on older versions of Windows 10, see https://aka.ms/rsat
    
    Test-ADServiceAccount WebApp01
    

为未加入域的容器主机用例准备容器主机

对未加入域的容器主机使用用于 Windows 容器的 gMSA 时,每个容器主机都必须安装一个 ccg.exe 插件,该插件将用于检索在上一步中指定的便携式用户帐户和凭据。 这些插件对于用于保护便携式用户帐户凭据的机密存储而言是唯一的。 例如,需要使用不同的插件将帐户凭据存储在 Azure Key Vault,而不是存储在 Kubernetes 的机密存储中。

Windows 当前不提供内置的默认插件。 插件的安装说明将取决于特定的实现。 有关创建和注册 ccg.exe 插件的详细信息,请参阅 ICcgDomainAuthCredentials 接口

创建凭据规范

凭据规范文件是一个 JSON 文档,其中包含有关你希望容器使用的 gMSA 帐户的元数据。 通过将标识配置与容器映像分离,你可以对容器使用哪个 gMSA 进行更改,只需简单地交换凭据规范文件即可,无需更改代码。

凭据规范文件是使用已加入域的计算机上的 CredentialSpec PowerShell 模块创建的。 创建该文件后,可以将其复制到其他容器主机或容器业务流程协调程序。 凭据规范文件不包含任何机密(例如 gMSA 密码),因为容器主机代表容器来检索 gMSA。

Docker 会在 Docker 数据目录中的 CredentialSpecs 目录下查找凭据规范文件。 在默认安装中,可以在 C:\ProgramData\Docker\CredentialSpecs 中找到此文件夹。

若要在容器主机上创建凭据规范文件,请执行以下操作:

  1. 安装 RSAT AD PowerShell 工具

    • 对于 Windows Server,请运行 Install-WindowsFeature RSAT-AD-PowerShell
    • 对于 Windows 10 版本 1809 或更高版本,请运行 Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'
    • 对于较旧版本的 Windows 10,请参阅 https://aka.ms/rsat
  2. 运行以下 cmdlet 来安装最新版本的 CredentialSpec PowerShell 模块

    Install-Module CredentialSpec
    

    如果你的容器主机上没有 Internet 访问权限,请在连接到 Internet 的计算机上运行 Save-Module CredentialSpec,并将模块文件夹复制到 C:\Program Files\WindowsPowerShell\Modules 或容器主机上 $env:PSModulePath 中的其他位置。

  3. 运行以下 cmdlet 来创建新的凭据规范文件:

    # Replace 'WebApp01' with your own gMSA 
    New-CredentialSpec -AccountName WebApp01
    

    默认情况下,该 cmdlet 将使用提供的 gMSA 名称作为容器的计算机帐户来创建凭据规范。 该文件将使用 gMSA 域和帐户名称作为文件名保存在 Docker CredentialSpecs 目录中。

    如果要将文件保存到其他目录,请使用 -Path 参数:

    New-CredentialSpec -AccountName WebApp01 -Path "C:\MyFolder\WebApp01_CredSpec.json"
    

    如果要在容器中以另一个 gMSA 的身份运行服务或进程,则还可以创建包含其他 gMSA 帐户的凭据规范。 若要执行该操作,请使用 -AdditionalAccounts 参数:

    New-CredentialSpec -AccountName WebApp01 -AdditionalAccounts LogAgentSvc, OtherSvc
    

    若要获取支持的参数的完整列表,请运行 Get-Help New-CredentialSpec -Full

  4. 可以使用以下 cmdlet 显示所有凭据规范的列表及其完整路径:

    Get-CredentialSpec
    

下面是凭据规范的示例:

{
    "CmsPlugins": [
        "ActiveDirectory"
    ],
    "DomainJoinConfig": {
        "Sid": "S-1-5-21-702590844-1001920913-2680819671",
        "MachineAccountName": "webapp01",
        "Guid": "56d9b66c-d746-4f87-bd26-26760cfdca2e",
        "DnsTreeName": "contoso.com",
        "DnsName": "contoso.com",
        "NetBiosName": "CONTOSO"
    },
    "ActiveDirectoryConfig": {
        "GroupManagedServiceAccounts": [
            {
                "Name": "webapp01",
                "Scope": "contoso.com"
            },
            {
                "Name": "webapp01",
                "Scope": "CONTOSO"
            }
        ]
    }
}

未加入域的容器主机用例的其他凭据规范配置

将 gMSA 用于未加入域的容器主机时,需要将以后将使用的有关 ccg.exe 插件的信息添加到凭据规范。此信息将添加到称为HostAccountConfig 的凭据规范部分。 HostAccountConfig 部分包含三个需要填充的字段:

  • PortableCcgVersion:应设置为“1”。
  • PluginGUID:ccg.exe 插件的 COM CLSID。 这对于所使用的插件是唯一的。
  • PluginInput:插件特定的输入,用于从机密存储中检索用户帐户信息。

下面是已添加 HostAccountConfig 部分的凭据规范的示例:

{
    "CmsPlugins": [
        "ActiveDirectory"
    ],
    "DomainJoinConfig": {
        "Sid": "S-1-5-21-702590844-1001920913-2680819671",
        "MachineAccountName": "webapp01",
        "Guid": "56d9b66c-d746-4f87-bd26-26760cfdca2e",
        "DnsTreeName": "contoso.com",
        "DnsName": "contoso.com",
        "NetBiosName": "CONTOSO"
    },
    "ActiveDirectoryConfig": {
        "GroupManagedServiceAccounts": [
            {
                "Name": "webapp01",
                "Scope": "contoso.com"
            },
            {
                "Name": "webapp01",
                "Scope": "CONTOSO"
            }
        ],
        "HostAccountConfig": {
            "PortableCcgVersion": "1",
            "PluginGUID": "{GDMA0342-266A-4D1P-831J-20990E82944F}",
            "PluginInput": "contoso.com:gmsaccg:<password>"
        }
    }
}

后续步骤

设置 gMSA 帐户后,可以使用它来执行以下操作:

如果在设置过程中遇到任何问题,请查看我们的故障排除指南,了解可能的解决方案。

其他资源