Habilitar GMSA (Contas de Serviço Gerenciado de Grupo) para seus nós do Windows Server no cluster do AKS (Serviço de Kubernetes do Azure)

Uma GMSA (Conta de Serviço Gerenciado de Grupo) é uma conta de domínio gerenciado para vários servidores que fornece gerenciamento automático de senhas, gerenciamento de SPN (nome da entidade de serviço) simplificado e a capacidade de delegar o gerenciamento para outros administradores. O AKS fornece a capacidade de habilitar o GMSA em seus nós do Windows Server, o que permite que os contêineres em execução em nós do Windows Server se integrem e sejam gerenciados pelo GMSA.

Pré-requisitos

A habilitação do GMSA com nós do Windows Server no AKS exige:

  • Kubernetes 1.19 ou posterior.
  • CLI do Azure versão 2.35.0 ou superior
  • Identidades gerenciadas com o seu cluster do AKS.
  • Permissões para criar ou atualizar um Azure Key Vault.
  • Permissões para configurar o GMSA no Domínio do Active Directory ou no Active Directory local.
  • O controlador de domínio precisa ter os Serviços Web do Active Directory habilitados e precisa estar acessível na porta 9389 pelo cluster do AKS.

Configurar GMSA no controlador de domínio do Active Directory

Para usar o GMSA com o AKS, você precisa do GMSA e de uma credencial de usuário de domínio padrão para acessar a credencial de GMSA configurada no controlador de domínio. Para configurar o GMSA em seu controlador de domínio, confira Introdução a Contas de Serviço Gerenciado de Grupo. Para a credencial de usuário de domínio padrão, você pode usar um usuário existente ou criar um, desde que ele tenha acesso à credencial GMSA.

Importante

Você precisa usar o Domínio do Active Directory ou o Active Directory local. No momento, você não pode usar o Azure Active Directory para configurar o GMSA com um cluster do AKS.

Armazene as credenciais de usuário de domínio padrão no Azure Key Vault

O cluster do AKS usa as credenciais de usuário de domínio padrão para acessar as credenciais GMSA do controlador de domínio. Para fornecer acesso seguro a essas credenciais para o cluster do AKS, essas credenciais devem ser armazenadas no Azure Key Vault. Você pode criar um cofre de chaves ou usar um existente.

Use az keyvault secret set para armazenar a credencial de usuário de domínio padrão como um segredo em seu cofre de chaves. O exemplo a seguir armazena a credencial de usuário do domínio com a chave GMSADomainUserCred no cofre de chaves MyAKSGMSAVault. Você deve substituir os parâmetros por seu próprio cofre de chaves, chave e credencial de usuário de domínio.

az keyvault secret set --vault-name MyAKSGMSAVault --name "GMSADomainUserCred" --value "$Domain\\$DomainUsername:$DomainUserPassword"

Observação

Use o nome de domínio totalmente qualificado para o domínio em vez do nome de domínio parcialmente qualificado que pode ser usado em redes internas.

O comando acima faz o escape do parâmetro value para executar a CLI do Azure em um shell do Linux. Ao executar o comando da CLI do Azure no Windows PowerShell, você não precisa fazer o escape de caracteres no parâmetro value.

Opcional: usar uma VNET personalizada com DNS personalizado

O controlador de domínio precisa ser configurado por meio do DNS para que ele seja acessível pelo cluster do AKS. Você pode configurar a rede e o DNS fora do cluster do AKS para permitir que o cluster acesse o controlador de domínio. Como alternativa, você pode configurar uma VNET personalizada com um DNS personalizado usando a CNI do Azure com o cluster do AKS para fornecer acesso ao controlador de domínio. Para obter mais detalhes, confira Configurar rede a CNI do Azure no AKS (Serviço de Kubernetes do Azure).

Opcional: usar sua própria identidade de kubelet para o cluster

Para fornecer ao cluster do AKS acesso ao cofre de chaves, a identidade do kubelet do cluster precisa de acesso ao cofre de chaves. Por padrão, quando você cria um cluster com a identidade gerenciada habilitada, uma identidade de kubelet é criada automaticamente. Você pode permitir acesso ao cofre de chaves para essa identidade após a criação do cluster, o que é feito em uma etapa posterior.

Como alternativa, você pode criar uma identidade própria e usar essa identidade durante a criação do cluster em uma etapa posterior. Para obter mais detalhes sobre as identidades gerenciadas fornecidas, confira Resumo das identidades gerenciadas.

Para criar uma identidade própria, use az identity create. O exemplo a seguir cria uma identidade myIdentity no grupo de recursos myResourceGroup.

az identity create --name myIdentity --resource-group myResourceGroup

Você pode permitir ao seu kubelet acesso de identidade ao cofre de chaves antes ou depois de criar o cluster. O exemplo a seguir usa az identity list para obter a ID da identidade e defini-la como MANAGED_ID, depois usa az keyvault set-policy para conceder acesso de identidade ao cofre de chaves MyAKSGMSAVault.

MANAGED_ID=$(az identity list --query "[].id" -o tsv)
az keyvault set-policy --name "MyAKSGMSAVault" --object-id $MANAGED_ID --secret-permissions get

Criar cluster AKS

Para usar GMSA com o cluster do AKS, use os parâmetros enable-windows-gmsa, gmsa-dns-server, gmsa-root-domain-name e enable-managed-identity.

Observação

Ao criar um cluster com pools de nós do Windows Server, você precisa especificar as credenciais de administrador ao criar o cluster. Os comandos a seguir solicitam um nome de usuário e o definem como WINDOWS_USERNAME para uso em um comando posterior (lembre-se de que os comandos deste artigo são inseridos em um shell do BASH).

echo "Please enter the username to use as administrator credentials for Windows Server nodes on your cluster: " && read WINDOWS_USERNAME

Use az aks create para criar um cluster do AKS e, em seguida, az aks nodepool add para adicionar um pool de nós do Windows Server. O exemplo a seguir cria um cluster MyAKS no grupo de recursos MyResourceGroup, habilita o GMSA e adiciona um novo pool de nós chamado npwin.

Observação

Se você estiver usando uma vnet personalizada, também precisará especificar a ID da vnet usando vnet-subnet-id e talvez precise também adicionar docker-bridge-address, dns-service-ip e service-cidr dependendo da sua configuração.

Se você criou sua própria identidade para a identidade do kubelet, use o parâmetro assign-kubelet-identity para especificar sua identidade.

DNS_SERVER=<IP address of DNS server>
ROOT_DOMAIN_NAME="contoso.com"

az aks create \
    --resource-group MyResourceGroup \
    --name MyAKS \
    --vm-set-type VirtualMachineScaleSets \
    --network-plugin azure \
    --load-balancer-sku standard \
    --windows-admin-username $WINDOWS_USERNAME \
    --enable-managed-identity \
    --enable-windows-gmsa \
    --gmsa-dns-server $DNS_SERVER \
    --gmsa-root-domain-name $ROOT_DOMAIN_NAME

az aks nodepool add \
    --resource-group myResourceGroup \
    --cluster-name myAKS \
    --os-type Windows \
    --name npwin \
    --node-count 1    

Você também pode habilitar o GMSA em clusters existentes que já têm identidades gerenciadas e nós do Windows Server habilitados usando az aks update. Por exemplo:

az aks update \
    --resource-group MyResourceGroup \
    --name MyAKS \
    --enable-windows-gmsa \
    --gmsa-dns-server $DNS_SERVER \
    --gmsa-root-domain-name $ROOT_DOMAIN_NAME

Depois de criar o cluster ou atualizá-lo, use az keyvault set-policy para permitir acesso de identidade ao cofre de chaves. O exemplo a seguir concede à identidade de kubelet criada pelo cluster acesso ao cofre de chaves MyAKSGMSAVault.

Observação

Se você forneceu sua identidade para a identidade do kubelet, ignore esta etapa.

MANAGED_ID=$(az aks show -g MyResourceGroup -n MyAKS --query "identityProfile.kubeletidentity.objectId" -o tsv)

az keyvault set-policy --name "MyAKSGMSAVault" --object-id $MANAGED_ID --secret-permissions get

Instalar a especificação de credenciais GMSA

Para configurar o kubectl para se conectar ao cluster do Kubernetes, use o comando az aks get-credentials. O seguinte exemplo obtém as credenciais para o cluster AKS chamado MyAKS no MyResourceGroup:

az aks get-credentials --resource-group MyResourceGroup --name MyAKS

Crie um gmsa-spec.yaml com o código a seguir, substituindo os espaço reservados por seus valores.

apiVersion: windows.k8s.io/v1alpha1
kind: GMSACredentialSpec
metadata:
  name: aks-gmsa-spec  # This name can be changed, but it will be used as a reference in the pod spec
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: $GMSA_ACCOUNT_USERNAME
      Scope: $NETBIOS_DOMAIN_NAME
    - Name: $GMSA_ACCOUNT_USERNAME
      Scope: $DNS_DOMAIN_NAME
    HostAccountConfig:
      PluginGUID: '{CCC2A336-D7F3-4818-A213-272B7924213E}'
      PortableCcgVersion: "1"
      PluginInput: ObjectId=$MANAGED_ID;SecretUri=$SECRET_URI  # SECRET_URI takes the form https://$akvName.vault.azure.net/secrets/$akvSecretName
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: $DNS_DOMAIN_NAME
    DnsTreeName: $DNS_ROOT_DOMAIN_NAME
    Guid:  $AD_DOMAIN_OBJECT_GUID
    MachineAccountName: $GMSA_ACCOUNT_USERNAME
    NetBiosName: $NETBIOS_DOMAIN_NAME
    Sid: $GMSA_SID

Crie um gmsa-role.yaml com o código a seguir.

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

Crie um gmsa-role-binding.yaml com o código a seguir.

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

Use kubectl apply para aplicar as alterações de gmsa-spec.yaml, gmsa-role.yaml e gmsa-role-binding.yaml.

kubectl apply -f gmsa-spec.yaml
kubectl apply -f gmsa-role.yaml
kubectl apply -f gmsa-role-binding.yaml

Verifique se o GMSA está instalado e funcionando

Crie um gmsa-demo.yaml com o código a seguir.

---
kind: ConfigMap
apiVersion: v1
metadata:
  labels:
   app: gmsa-demo
  name: gmsa-demo
  namespace: default
data:
  run.ps1: |
   $ErrorActionPreference = "Stop"

   Write-Output "Configuring IIS with authentication."

   # Add required Windows features, since they are not installed by default.
   Install-WindowsFeature "Web-Windows-Auth", "Web-Asp-Net45"

   # Create simple ASP.Net page.
   New-Item -Force -ItemType Directory -Path 'C:\inetpub\wwwroot\app'
   Set-Content -Path 'C:\inetpub\wwwroot\app\default.aspx' -Value 'Authenticated as <B><%=User.Identity.Name%></B>, Type of Authentication: <B><%=User.Identity.AuthenticationType%></B>'

   # Configure IIS with authentication.
   Import-Module IISAdministration
   Start-IISCommitDelay
   (Get-IISConfigSection -SectionPath 'system.webServer/security/authentication/windowsAuthentication').Attributes['enabled'].value = $true
   (Get-IISConfigSection -SectionPath 'system.webServer/security/authentication/anonymousAuthentication').Attributes['enabled'].value = $false
   (Get-IISServerManager).Sites[0].Applications[0].VirtualDirectories[0].PhysicalPath = 'C:\inetpub\wwwroot\app'
   Stop-IISCommitDelay

   Write-Output "IIS with authentication is ready."

   C:\ServiceMonitor.exe w3svc
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: gmsa-demo
  name: gmsa-demo
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gmsa-demo
  template:
    metadata:
      labels:
        app: gmsa-demo
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: aks-gmsa-spec
      containers:
      - name: iis
        image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: IfNotPresent
        command:
         - powershell
        args:
          - -File
          - /gmsa-demo/run.ps1
        volumeMounts:
          - name: gmsa-demo
            mountPath: /gmsa-demo
      volumes:
      - configMap:
          defaultMode: 420
          name: gmsa-demo
        name: gmsa-demo
      nodeSelector:
        kubernetes.io/os: windows
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: gmsa-demo
  name: gmsa-demo
  namespace: default
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: gmsa-demo
  type: LoadBalancer

Use kubectl apply para aplicar as alterações de gmsa-demo.yaml

kubectl apply -f gmsa-demo.yaml

Use kubectl get service para exibir o endereço IP do aplicativo de exemplo.

kubectl get service gmsa-demo --watch

Inicialmente, o EXTERNAL-IP para o serviço gmsa-demo é mostrado como pendente.

NAME               TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
gmsa-demo          LoadBalancer   10.0.37.27   <pending>     80:30572/TCP   6s

Quando o endereço EXTERNAL-IP for alterado de pendente para um endereço IP público real, use CTRL-C para interromper o processo de inspeção do kubectl. A seguinte saída de exemplo mostra um endereço IP público válido atribuído ao serviço:

gmsa-demo  LoadBalancer   10.0.37.27   EXTERNAL-IP   80:30572/TCP   2m

Para verificar se o GMSA está funcionando e configurado corretamente, abra um navegador da Web para o endereço IP externo do serviço gmsa-demo. Autentique-se com $NETBIOS_DOMAIN_NAME\$AD_USERNAME e a senha e confirme se você vê Authenticated as $NETBIOS_DOMAIN_NAME\$AD_USERNAME, Type of Authentication: Negotiate.

Solução de problemas

Nenhuma autenticação é solicitada ao carregar a página

Se a página for carregada, mas você não for solicitado a autenticar, use kubelet logs POD_NAME para exibir os logs do pod e verificar se você vê a mensagem O IIS com autenticação está pronto.

Tempo limite da conexão atingido ao tentar carregar a página

Se você receber uma mensagem de tempo limite de conexão ao tentar carregar a página, verifique se o aplicativo de exemplo está em execução com kubectl get pods --watch. Às vezes, o endereço IP externo para o serviço de aplicativo de exemplo está disponível antes de o pod do aplicativo de exemplo ser executado.

O pod falha em iniciar e um erro winapi aparece nos eventos de pod

Depois de executar kubectl get pods --watch e aguardar vários minutos, se o pod não for iniciar, execute kubectl describe pod POD_NAME. Se você vir um erro winapi nos eventos de pod, provavelmente será um erro na configuração de especificação de credenciais GMSA. Verifique se todos os valores de substituição em gmsa-spec.yaml estão corretos, execute kubectl apply -f gmsa-spec.yaml novamente e reimplante o aplicativo de exemplo.