为 Windows Pod 和容器配置 GMSA

特性状态: Kubernetes v1.18 [stable]

本页面展示如何为将在 Windows 节点上运行的 Pod 和容器配置 组托管服务账户 (GMSA)。组托管服务账户是一种特定类型的 Active Directory 账户,提供自动密码管理、简化的服务主体名称 (SPN) 管理,以及将管理权限委托给多个服务器上的其他管理员的能力。

在 Kubernetes 中,GMSA 凭据规范以 Kubernetes 集群范围的自定义资源形式进行配置。Windows Pod 以及 Pod 内的单个容器可以配置为使用 GMSA,以便在与其他 Windows 服务交互时执行基于域的功能(例如 Kerberos 身份验证)。

准备工作

你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与你的集群通信。集群应包含 Windows 工作节点。本节介绍每个集群仅需执行一次的初始步骤。

安装 GMSACredentialSpec CRD

集群上需要配置 GMSA 凭据规范资源的 CustomResourceDefinition (CRD),以定义自定义资源类型 GMSACredentialSpec。下载 GMSA CRD YAML 并将其保存为 gmsa-crd.yaml。接下来,使用 kubectl apply -f gmsa-crd.yaml 安装 CRD。

安装 Webhook 以验证 GMSA 用户

需要在 Kubernetes 集群上配置两个 Webhook,以在 Pod 或容器级别填充和验证 GMSA 凭据规范引用:

  1. 一个变异 Webhook,用于将对 GMSA 的引用(通过 Pod 规范中的名称)扩展为 Pod 规范中 JSON 格式的完整凭据规范。

  2. 一个验证 Webhook,确保对 GMSA 的所有引用都被 Pod 服务账户授权使用。

安装上述 Webhook 及相关对象需要以下步骤:

  1. 创建证书密钥对(用于允许 Webhook 容器与集群通信)。

  2. 使用上述证书安装一个 Secret。

  3. 为核心 Webhook 逻辑创建部署。

  4. 创建引用部署的验证和变异 Webhook 配置。

可以使用脚本来部署和配置上述 GMSA Webhook 及相关对象。脚本可以带 --dry-run=server 选项运行,以便你查看将对集群进行的更改。

脚本使用的 YAML 模板 也可以用于手动部署 Webhook 和相关对象(参数需要适当替换)。

在 Active Directory 中配置 GMSA 和 Windows 节点

在 Kubernetes 中的 Pod 可以配置为使用 GMSA 之前,需要在 Active Directory 中配置所需的 GMSA,如 Windows GMSA 文档 中所述。Windows 工作节点(作为 Kubernetes 集群的一部分)需要在 Active Directory 中进行配置,以访问与所需 GMSA 关联的秘密凭据,如 Windows GMSA 文档 中所述。

创建 GMSA 凭据规范资源

安装 GMSACredentialSpec CRD 后(如前所述),可以配置包含 GMSA 凭据规范的自定义资源。GMSA 凭据规范不包含秘密或敏感数据。它是容器运行时可以用来描述容器所需的 GMSA 给 Windows 的信息。GMSA 凭据规范可以使用一个实用 PowerShell 脚本 生成 YAML 格式。

以下是手动生成 JSON 格式的 GMSA 凭据规范 YAML,然后进行转换的步骤:

  1. 导入 CredentialSpec 模块ipmo CredentialSpec.psm1

  2. 使用 New-CredentialSpec 创建 JSON 格式的凭据规范。要创建名为 WebApp1 的 GMSA 凭据规范,请调用 New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer)

  3. 使用 Get-CredentialSpec 显示 JSON 文件的路径。

  4. 将凭据规范文件从 JSON 转换为 YAML 格式,并应用必要的头字段 apiVersionkindmetadatacredspec,使其成为可在 Kubernetes 中配置的 GMSACredentialSpec 自定义资源。

以下 YAML 配置描述了一个名为 gmsa-WebApp1 的 GMSA 凭据规范:

apiVersion: windows.k8s.io/v1
kind: GMSACredentialSpec
metadata:
  name: gmsa-WebApp1  # This is an arbitrary name but it will be used as a reference
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: WebApp1   # Username of the GMSA account
      Scope: CONTOSO  # NETBIOS Domain Name
    - Name: WebApp1   # Username of the GMSA account
      Scope: contoso.com # DNS Domain Name
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: contoso.com  # DNS Domain Name
    DnsTreeName: contoso.com # DNS Domain Name Root
    Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a  # GUID of the Domain
    MachineAccountName: WebApp1 # Username of the GMSA account
    NetBiosName: CONTOSO  # NETBIOS Domain Name
    Sid: S-1-5-21-2126449477-2524075714-3094792973 # SID of the Domain

上述凭证规范资源可以保存为 gmsa-Webapp1-credspec.yaml,并使用以下命令应用到集群:kubectl apply -f gmsa-Webapp1-credspec.yml

配置集群角色以在特定 GMSA 凭证规范上启用 RBAC

需要为每个 GMSA 凭证规范资源定义一个集群角色。这授权了主题(通常是服务账户)在特定 GMSA 资源上使用 use 动词。以下示例显示了一个授权使用上述 gmsa-WebApp1 凭证规范的集群角色。将文件保存为 gmsa-webapp1-role.yaml,并使用 kubectl apply -f gmsa-webapp1-role.yaml 应用。

# Create the Role to read the credspec
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
  resources: ["gmsacredentialspecs"]
  verbs: ["use"]
  resourceNames: ["gmsa-WebApp1"]

为服务账户分配角色以使用特定的 GMSA credspecs

一个服务账户(Pod 将配置使用该账户)需要绑定到上面创建的集群角色。这授权服务账户使用所需的 GMSA 凭证规范资源。以下显示了默认服务账户绑定到集群角色 webapp1-role,以使用上面创建的 gmsa-WebApp1 凭证规范资源。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-default-svc-account-read-on-gmsa-WebApp1
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: webapp1-role
  apiGroup: rbac.authorization.k8s.io

在 Pod 规范中配置 GMSA 凭据规范引用

Pod 规范字段 securityContext.windowsOptions.gmsaCredentialSpecName 用于在 Pod 规范中指定对所需 GMSA 凭据规范自定义资源的引用。这会配置 Pod 规范中的所有容器使用指定的 GMSA。一个填充了引用 gmsa-WebApp1 注解的示例 Pod 规范:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: gmsa-webapp1
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
      nodeSelector:
        kubernetes.io/os: windows

Pod 规范中的单个容器也可以使用每个容器的 securityContext.windowsOptions.gmsaCredentialSpecName 字段指定所需的 GMSA 凭证规范。例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
        securityContext:
          windowsOptions:
            gmsaCredentialSpecName: gmsa-Webapp1
      nodeSelector:
        kubernetes.io/os: windows

当带有 GMSA 字段的 Pod 规范(如上所述)在集群中应用时,会发生以下事件序列:

  1. 变异 Webhook 解析并展开对 GMSA 凭据规范资源的所有引用到 GMSA 凭据规范的内容中。

  2. 验证 Webhook 确保与 Pod 关联的服务账户被授权在指定的 GMSA 凭证规范上使用 use 动词。

  3. 容器运行时使用指定的 GMSA 凭证规范配置每个 Windows 容器,以便容器可以承担 Active Directory 中 GMSA 的身份,并使用该身份访问域中的服务。

使用主机名或 FQDN 验证网络共享

如果你在 Pod 中使用主机名或 FQDN 连接 SMB 共享时遇到问题,但可以通过 IPv4 地址访问共享,请确保 Windows 节点上设置了以下注册表项。

reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1

之后,需要重新创建运行中的 Pod 以使其拾取行为更改。有关此注册表项如何使用的更多信息,请参阅此处

故障排除

如果您在环境中配置 GMSA 遇到困难,可以采取以下故障排除步骤。

首先,确保 credspec 已传递给 Pod。为此,您需要 exec 进入其中一个 Pod 并检查 nltest.exe /parentdomain 命令的输出。

在下面的示例中,Pod 未能正确获取 credspec:

kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

nltest.exe /parentdomain 命令导致以下错误:

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

如果你的 Pod 正确获得了凭据规范,那么接下来检查与域的通信。首先,从你的 Pod 内部,快速执行 nslookup 来查找你的域的根。

这将告诉我们三件事:

  1. Pod 可以访问域控制器 (DC)
  2. DC 可以访问 Pod
  3. DNS 正在正常工作。

如果 DNS 和通信测试通过,接下来你需要检查 Pod 是否已与域建立安全通道通信。为此,再次 exec 进入你的 Pod 并运行 nltest.exe /query 命令。

nltest.exe /query

结果如下:

I_NetLogonControl failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

这告诉我们,由于某种原因,Pod 无法使用凭证规范中指定的帐户登录到域。你可以尝试通过运行以下命令来修复安全通道:

nltest /sc_reset:domain.example

如果命令成功,你将看到类似这样的输出:

Flags: 30 HAS_IP  HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

如果上述方法纠正了错误,你可以通过将以下生命周期钩子添加到 Pod 规范中来自动化该步骤。如果它没有纠正错误,你需要再次检查你的 credspec 并确认它是否正确和完整。

        image: registry.domain.example/iis-auth:1809v1
        lifecycle:
          postStart:
            exec:
              command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
        imagePullPolicy: IfNotPresent

如果你将上面显示的 lifecycle 部分添加到 Pod 规范中,Pod 将执行列出的命令以重启 netlogon 服务,直到 nltest.exe /query 命令无错误退出。

上次修改时间:2024 年 7 月 10 日太平洋标准时间下午 2:05:修复 gMSA 凭证规范定义 (97bd20e5a9)