你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

在Azure Kubernetes 服务 (AKS)群集上部署和配置Microsoft Entra Workload ID

本文介绍如何使用 Microsoft Entra Workload ID 部署和配置Azure Kubernetes 服务 (AKS)群集。 本文中的步骤包括:

  • 使用具有 OpenID Connect(OIDC)颁发者的 Azure CLI 或 Terraform 创建新的或更新现有 AKS 群集,并启用Microsoft Entra Workload ID。
  • 创建工作负荷标识和 Kubernetes 服务帐户。
  • 配置托管标识以进行令牌联合身份验证。
  • 部署工作负载并使用工作负载标识验证身份验证。
  • (可选)向群集中的 Pod 授予对Azure密钥保管库中的机密的访问权限。

Prerequisites

  • 如果没有Azure帐户,请在开始前创建一个 free 帐户
  • 本文需要 2.47.0 或更高版本的 Azure CLI。 如果使用Azure Cloud Shell,则已安装最新版本。 运行 az --version 即可查找版本。 如果需要安装或升级,请参阅 Install Azure CLI
  • 确保用于创建群集的标识具有合适的的最低权限。 有关详细信息,请参阅 Azure Kubernetes 服务 (AKS) 的访问和身份选项
  • 如果有多个Azure订阅,请选择相应的订阅 ID,其中应使用 az account set 命令对资源计费。
  • 在本地安装 Terraform。 有关安装说明,请参阅 安装 Terraform

注意

可以使用服务连接器自动配置某些步骤。 有关详细信息,请参阅 Tutorial:使用 Microsoft Entra Workload ID 和 Service Connector 连接到 Azure Kubernetes 服务 (AKS) 中的 Azure 存储帐户

创建 Terraform 配置文件

Terraform 配置文件定义 Terraform 创建和管理的基础结构。

  1. 创建名为 main.tf 的文件,并添加以下代码以定义 Terraform 版本并指定Azure提供程序:

    terraform {
     required_version = ">= 1.5.0"
     required_providers {
       azurerm = {
         source  = "hashicorp/azurerm"
         version = "~> 4.0"
       }
       kubernetes = {
         source  = "hashicorp/kubernetes"
         version = "~> 2.30"
       }
       random = {
         source  = "hashicorp/random"
         version = "~> 3.6"
       }
     }
    }
    provider "azurerm" {
     features {}
     subscription_id = var.subscription_id
    }
    data "azurerm_client_config" "current" {}
    
  2. 添加以下代码以 main.tf 定义可重用变量并为所有资源生成唯一名称:

    resource "random_string" "suffix" {
     length  = 6
     upper   = false
     special = false
     numeric = true
    }
    locals {
     suffix = random_string.suffix.result
     resource_group_name       = "rg-aks-wi-${local.suffix}"
     cluster_name              = "akswi${local.suffix}"
     managed_identity_name     = "uami-wi-${local.suffix}"
     federated_credential_name = "fic-wi-${local.suffix}"
     key_vault_name            = lower(substr("kvwi${local.suffix}", 0, 24))
     secret_name               = "secret-${local.suffix}"
     service_account_name      = "workload-sa-${local.suffix}"
     service_account_namespace = "default"
     workload_identity_subject = "system:serviceaccount:${local.service_account_namespace}:${local.service_account_name}"
    }
    

创建资源组

使用 az group create 命令创建资源组。

export RANDOM_ID="$(openssl rand -hex 3)"
export RESOURCE_GROUP="myResourceGroup$RANDOM_ID"
export LOCATION="<your-preferred-region>"
az group create --name "${RESOURCE_GROUP}" --location "${LOCATION}"

将以下代码添加到 main.tf,以创建Azure资源组。 更新 location 值以匹配首选Azure区域。

resource "azurerm_resource_group" "this" {
 name     = local.resource_group_name
 location = "eastus"
}

在 AKS 群集上启用 OIDC 颁发者和 Microsoft Entra 工作负荷 ID

可以在新的或现有的 AKS 群集上启用 OIDC 颁发者和 Microsoft Entra 工作负荷 ID。

使用带有 az aks create 参数的 --enable-oidc-issuer 命令创建 AKS 群集,以启用 OIDC 颁发者和 --enable-workload-identity 参数以启用Microsoft Entra Workload ID。 以下示例会创建具有一个节点的群集:

export CLUSTER_NAME="myAKSCluster$RANDOM_ID"
az aks create \
    --resource-group "${RESOURCE_GROUP}" \
    --name "${CLUSTER_NAME}" \
    --enable-oidc-issuer \
    --enable-workload-identity \
    --generate-ssh-keys

片刻之后,该命令将会完成,并返回有关群集的 JSON 格式信息。

将以下代码添加到 main.tf,以创建一个启用了 OIDC 颁发者和 Microsoft Entra Workload ID 的 AKS 群集:

resource "azurerm_kubernetes_cluster" "this" {
 name                              = local.cluster_name
 location                          = azurerm_resource_group.this.location
 resource_group_name               = azurerm_resource_group.this.name
 dns_prefix                        = local.cluster_name
 oidc_issuer_enabled               = true
 workload_identity_enabled         = true
 role_based_access_control_enabled = true
 default_node_pool {
   name       = "system"
   node_count = 1
   vm_size    = "Standard_B4ms"
 }
 identity {
   type = "SystemAssigned"
 }
}

检索 OIDC 颁发者 URL

使用 az aks show 命令获取 OIDC 颁发者 URL,并将其保存到环境变量。

export AKS_OIDC_ISSUER="$(az aks show --name "${CLUSTER_NAME}" \
    --resource-group "${RESOURCE_GROUP}" \
    --query "oidcIssuerProfile.issuerUrl" \
    --output tsv)"

环境变量应包含类似于以下示例的证书颁发者 URL:

https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111/

默认情况下,颁发者设置为使用基 URL https://{region}.oic.prod-aks.azure.com/{tenant_id}/{uuid},其中 {region} 的值与部署 AKS 群集的位置相匹配。 该值 {uuid} 表示 OIDC 密钥,这是每个群集随机生成的不可变 GUID。

main.tf 中添加以下代码以检索 OIDC 颁发者 URL:

output "oidc_issuer_url" {
 value = azurerm_kubernetes_cluster.this.oidc_issuer_url
}

创建托管标识

  1. 获取订阅 ID,并使用命令将其保存到环境变量 az account show

    export SUBSCRIPTION="$(az account show --query id --output tsv)"
    
  2. 使用 az identity create 命令创建用户分配的托管标识。

    export USER_ASSIGNED_IDENTITY_NAME="myIdentity$RANDOM_ID"
    az identity create \
        --name "${USER_ASSIGNED_IDENTITY_NAME}" \
        --resource-group "${RESOURCE_GROUP}" \
        --location "${LOCATION}" \
        --subscription "${SUBSCRIPTION}"
    

    以下输出示例演示如何成功创建托管标识:

    {
      "clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroupxxxxxx/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentityxxxxxx",
      "location": "eastus",
      "name": "myIdentityxxxxxx",
      "principalId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "resourceGroup": "myResourceGroupxxxxxx",
      "systemData": null,
      "tags": {},
      "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
    }
    
  3. 获取托管标识的客户端 ID,并使用命令将其保存到环境变量 az identity show

    export USER_ASSIGNED_CLIENT_ID="$(az identity show \
        --resource-group "${RESOURCE_GROUP}" \
        --name "${USER_ASSIGNED_IDENTITY_NAME}" \
        --query 'clientId' \
        --output tsv)"
    

将以下代码添加到 main.tf 来创建托管标识:

resource "azurerm_user_assigned_identity" "this" {
 name                = local.managed_identity_name
 location            = azurerm_resource_group.this.location
 resource_group_name = azurerm_resource_group.this.name
}

创建 Kubernetes 服务帐户

  1. 使用 az aks get-credentials 命令连接到 AKS 群集。

    az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}"
    
  2. 使用 kubectl apply 命令应用以下清单,创建 Kubernetes 服务帐户,并使用托管标识的客户端 ID 对该帐户进行注解。

    export SERVICE_ACCOUNT_NAME="workload-identity-sa$RANDOM_ID"
    export SERVICE_ACCOUNT_NAMESPACE="default"
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        azure.workload.identity/client-id: "${USER_ASSIGNED_CLIENT_ID}"
      name: "${SERVICE_ACCOUNT_NAME}"
      namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
    EOF
    

    成功创建工作负载标识后的输出如下所示:

    serviceaccount/workload-identity-sa created
    
  1. main.tf中添加以下代码,以配置 Kubernetes 访问,以允许创建 Kubernetes 资源。

    data "azurerm_kubernetes_cluster" "this" {
     name                = azurerm_kubernetes_cluster.this.name
     resource_group_name = azurerm_resource_group.this.name
    }
    provider "kubernetes" {
     host                   = data.azurerm_kubernetes_cluster.this.kube_config[0].host
     client_certificate     = base64decode(data.azurerm_kubernetes_cluster.this.kube_config[0].client_certificate)
     client_key             = base64decode(data.azurerm_kubernetes_cluster.this.kube_config[0].client_key)
     cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.this.kube_config[0].cluster_ca_certificate)
    }
    
  2. 添加以下代码到main.tf以创建 Kubernetes 服务帐户,并使用托管标识的客户端 ID 对其进行标注:

    resource "kubernetes_service_account" "this" {
     metadata {
       name      = local.service_account_name
       namespace = local.service_account_namespace
       annotations = {
         "azure.workload.identity/client-id" = azurerm_user_assigned_identity.this.client_id
       }
     }
    }
    

创建联合标识凭据

使用 az identity federated-credential create 命令在托管标识、服务帐户颁发者和使用者之间创建联合标识凭据。

export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity$RANDOM_ID"
az identity federated-credential create \
    --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} \
    --identity-name "${USER_ASSIGNED_IDENTITY_NAME}" \
    --resource-group "${RESOURCE_GROUP}" \
    --issuer "${AKS_OIDC_ISSUER}" \
    --subject system:serviceaccount:"${SERVICE_ACCOUNT_NAMESPACE}":"${SERVICE_ACCOUNT_NAME}" \
    --audience api://AzureADTokenExchange

注意

添加联合标识凭据后,传播联合标识凭据需要几秒钟时间。 如果在添加联合标识凭据后立即发出令牌请求,则在刷新缓存之前,请求可能会失败。 若要避免此问题,可以在添加联合标识凭据后添加轻微的延迟。

添加以下代码到 main.tf 中,以在托管标识、服务帐户颁发者和使用者之间创建联合身份凭据:

resource "azurerm_federated_identity_credential" "this" {
 name                = local.federated_credential_name
 resource_group_name = azurerm_resource_group.this.name
 parent_id           = azurerm_user_assigned_identity.this.id
 issuer              = azurerm_kubernetes_cluster.this.oidc_issuer_url
 subject             = local.workload_identity_subject
 audience            = ["api://AzureADTokenExchange"]
}

有关 Microsoft Entra 中的联合标识凭据的详细信息,请参阅Microsoft Entra ID 中联合标识凭据的概述

使用 Azure RBAC 授权创建密钥保管库

以下示例演示如何使用Azure基于角色的访问控制(Azure RBAC)权限模型向 Pod 授予对密钥保管库的访问权限。 有关 Azure 密钥保管库 的 Azure RBAC 权限模型的详细信息,请参阅使用 Azure RBAC 为应用程序授予访问 Azure 密钥保管库 的权限

  1. 使用 az keyvault create 命令创建启用了清除保护和Azure RBAC 授权的密钥保管库。 如果现有密钥保管库配置了清除保护和 Azure RBAC 授权,您也可以使用它。

    export KEYVAULT_NAME="keyvault-workload-id$RANDOM_ID" # Ensure the key vault name is between 3-24 characters
    az keyvault create \
        --name "${KEYVAULT_NAME}" \
        --resource-group "${RESOURCE_GROUP}" \
        --location "${LOCATION}" \
        --enable-purge-protection \
        --enable-rbac-authorization
    
  2. 获取密钥保管库资源 ID,并使用命令将其保存到环境变量 az keyvault show

    export KEYVAULT_RESOURCE_ID=$(az keyvault show --resource-group "${RESOURCE_GROUP}" \
        --name "${KEYVAULT_NAME}" \
        --query id \
        --output tsv)
    

将以下代码添加到 main.tf,以使用 Azure RBAC 授权创建密钥保管库:

resource "azurerm_key_vault" "this" {
 name                          = local.key_vault_name
 location                      = azurerm_resource_group.this.location
 resource_group_name           = azurerm_resource_group.this.name
 tenant_id                     = data.azurerm_client_config.current.tenant_id
 sku_name                      = "standard"
 rbac_authorization_enabled    = true
}

为密钥保管库管理分配 RBAC 权限

  1. 获取调用方对象 ID,并使用命令将其保存到环境变量 az ad signed-in-user show

    export CALLER_OBJECT_ID=$(az ad signed-in-user show --query id -o tsv)
    
  2. 将 Azure RBAC 密钥保管库 机密官角色分配给自己,以便可以使用 az role assignment create 命令在新key vault中创建机密。

    az role assignment create --assignee "${CALLER_OBJECT_ID}" \
        --role "Key Vault Secrets Officer" \
        --scope "${KEYVAULT_RESOURCE_ID}"
    

将以下代码添加到 main.tf 中,将 Azure RBAC 密钥保管库 Secrets Officer 角色分配给自己,以便可以在新的密钥保管库中创建机密,并将 密钥保管库 Secrets User 角色分配给用户分配的托管标识。

resource "azurerm_role_assignment" "user" {
 scope                = azurerm_key_vault.this.id
 role_definition_name = "Key Vault Secrets Officer"
 principal_id         = data.azurerm_client_config.current.object_id
}
resource "azurerm_role_assignment" "identity" {
 scope                = azurerm_key_vault.this.id
 role_definition_name = "Key Vault Secrets User"
 principal_id         = azurerm_user_assigned_identity.this.principal_id
}

创建和配置机密访问

  1. 使用 az keyvault secret set 命令在密钥保管库中创建一个机密。

    export KEYVAULT_SECRET_NAME="my-secret$RANDOM_ID"
    az keyvault secret set \
        --vault-name "${KEYVAULT_NAME}" \
        --name "${KEYVAULT_SECRET_NAME}" \
        --value "Hello\!"
    
  2. 获取用户分配的托管标识的主体 ID,并使用命令将其保存到环境变量 az identity show

    export IDENTITY_PRINCIPAL_ID=$(az identity show \
        --name "${USER_ASSIGNED_IDENTITY_NAME}" \
        --resource-group "${RESOURCE_GROUP}" \
        --query principalId \
        --output tsv)
    
  3. 使用 az role assignment create 命令将密钥保管库机密用户角色分配给用户分配的托管标识。 此步骤授予托管标识从密钥保管库读取机密的权限。

    az role assignment create \
        --assignee-object-id "${IDENTITY_PRINCIPAL_ID}" \
        --role "Key Vault Secrets User" \
        --scope "${KEYVAULT_RESOURCE_ID}" \
        --assignee-principal-type ServicePrincipal
    
  4. 使用 az keyvault show 命令为密钥保管库 URL 创建环境变量。

    export KEYVAULT_URL="$(az keyvault show \
        --resource-group "${RESOURCE_GROUP}" \
        --name ${KEYVAULT_NAME} \
        --query properties.vaultUri \
        --output tsv)"
    

添加以下代码到 main.tf,以在密钥保管库中创建密钥:

resource "azurerm_key_vault_secret" "this" {
 name         = local.secret_name
 value        = "Hello from Key Vault"
 key_vault_id = azurerm_key_vault.this.id
}

部署验证 Pod 并测试访问权限

  1. 部署一个 Pod,来验证工作负载标识是否能够访问密钥保管库中的机密。 以下示例使用 ghcr.io/azure/azure-workload-identity/msal-go 映像,其中包含使用 Microsoft Entra Workload ID 从Azure 密钥保管库检索机密的示例应用程序:

    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
        name: sample-workload-identity-key-vault
        namespace: ${SERVICE_ACCOUNT_NAMESPACE}
        labels:
            azure.workload.identity/use: "true"
    spec:
        serviceAccountName: ${SERVICE_ACCOUNT_NAME}
        containers:
          - image: ghcr.io/azure/azure-workload-identity/msal-go
            name: oidc
            env:
              - name: KEYVAULT_URL
                value: ${KEYVAULT_URL}
              - name: SECRET_NAME
                value: ${KEYVAULT_SECRET_NAME}
        nodeSelector:
            kubernetes.io/os: linux
    EOF
    
  2. 使用 Ready 命令等待 Pod 处于 kubectl wait 状态。

    kubectl wait --namespace ${SERVICE_ACCOUNT_NAMESPACE} --for=condition=Ready pod/sample-workload-identity-key-vault --timeout=120s
    
  3. 检查 Pod 中是否通过SECRET_NAME命令设置了kubectl describe环境变量。

    kubectl describe pod sample-workload-identity-key-vault | grep "SECRET_NAME:"
    

    如果成功,输出应类似于以下示例:

    SECRET_NAME: ${KEYVAULT_SECRET_NAME}
    
  4. 确认 pod 可以获取令牌,并通过 kubectl logs 命令访问资源。

    kubectl logs sample-workload-identity-key-vault
    

    如果成功,输出应类似于以下示例:

    I0114 10:35:09.795900       1 main.go:63] "successfully got secret" secret="Hello\\!"
    

    重要说明

    Azure RBAC 角色分配最多可能需要 10 分钟才能传播。 如果 Pod 无法访问机密,则可能需要等待角色分配传播。 有关详细信息,请参阅 Troubleshoot Azure RBAC

在 AKS 群集上禁用Microsoft Entra Workload ID

在已启用和配置的 AKS 群集上禁用Microsoft Entra Workload ID,使用 az aks update 命令使用 --disable-workload-identity 参数更新 AKS 群集。

az aks update \
    --resource-group "${RESOURCE_GROUP}" \
    --name "${CLUSTER_NAME}" \
    --disable-workload-identity

部署验证 Pod

main.tf 中添加以下代码,以部署一个使用工作负载标识访问密钥保管库中机密的验证 pod:

resource "kubernetes_pod" "test" {
 metadata {
   name      = "workload-identity-test"
   namespace = local.service_account_namespace
   labels = {
     "azure.workload.identity/use" = "true"
   }
 }
 spec {
   service_account_name = kubernetes_service_account.this.metadata[0].name
   container {
     name  = "test"
     image = "ghcr.io/azure/azure-workload-identity/msal-go"
     env {
       name  = "KEYVAULT_URL"
       value = azurerm_key_vault.this.vault_uri
     }
     env {
       name  = "SECRET_NAME"
       value = azurerm_key_vault_secret.this.name
     }
   }
 }
}

初始化 Terraform

使用main.tf命令在包含terraform init文件的目录中初始化 Terraform。 此命令下载用于通过 Terraform 管理 Azure 资源所需的 Azure 提供程序。

terraform init

创建 Terraform 执行计划

使用 terraform plan 命令创建 Terraform 执行计划。 此命令显示 Terraform 将在Azure订阅中创建或修改的资源。

terraform plan

应用 Terraform 配置

查看并确认执行计划后,使用 terraform apply 命令应用 Terraform 配置。 此命令创建或修改Azure订阅中main.tf文件中定义的资源。

terraform apply

验证部署

  1. 使用 az aks get-credentials 命令连接到 AKS 群集。

    az aks get-credentials --name <cluster-name> --resource-group <resource-group>
    
  2. 使用 kubectl get pods 命令检查验证 Pod 的状态。

  3. Pod 达到 Ready 状态后,使用 kubectl logs 命令检查 Pod 日志,验证它是否可以访问密钥保管库机密。

    kubectl logs workload-identity-test
    

在本文中,你部署了 Kubernetes 群集,并将其配置为使用 Microsoft Entra Workload ID,为应用程序工作负荷利用该凭据进行身份验证做准备。 现在,你已准备好部署应用程序并将其配置为将工作负荷标识与最新版本的 Azure Identity 客户端库配合使用。 如果无法重写应用程序以使用最新的客户端库版本,则可以设置应用程序 Pod,以使用托管标识和工作负荷标识作为短期迁移解决方案进行身份验证。

Service Connector 集成有助于简化 AKS 工作负载和Azure支持服务的连接配置。 它安全地处理身份验证和网络配置,并遵循连接到Azure服务的最佳做法。 有关详细信息,请参阅在 AKS 中使用 Microsoft Entra 工作负载标识连接到 Foundry 模型中的 Azure OpenAI服务连接器介绍