本文介绍如何使用 Microsoft Entra 工作负荷 ID 部署和配置 Azure Kubernetes 服务(AKS)群集。 本文中的步骤包括:
- 使用具有 OpenID Connect(OIDC)颁发者的 Azure CLI 或 Terraform 创建新的或更新现有 AKS 群集,并启用Microsoft Entra Workload ID。
- 创建工作负荷标识和 Kubernetes 服务帐户。
- 配置托管标识以进行令牌联合身份验证。
- 部署工作负载并使用工作负载标识验证身份。
- (可选)向群集中的 Pod 授予访问 Azure Key Vault 中机密的权限。
先决条件
- 如果没有 Azure 试用版订阅,请在开始前创建一个。
- 本文需要 2.47.0 或更高版本的 Azure CLI。
- 确保用于创建群集的标识具有合适的的最低权限。 有关详细信息,请参阅 Azure Kubernetes 服务 (AKS) 的访问和标识选项。
- 如果你有多个 Azure 订阅,请使用
az account set命令选择应在其中计收资源费用的相应订阅 ID。
- 在本地安装 Terraform。 有关安装说明,请参阅 安装 Terraform。
注意
可以使用服务连接器自动配置某些步骤。 有关详细信息,请参阅[教程:通过 Microsoft Entra 工作负载 ID 在 Azure Kubernetes 服务 (AKS) 中连接到 Azure 存储帐户][tutorial-python-aks-storage-workload-identity]。
创建 Terraform 配置文件
Terraform 配置文件定义 Terraform 创建和管理的基础结构。
创建名为
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" {}添加以下代码以
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 工作负荷 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
}
创建管理标识
获取订阅 ID,并使用命令将其保存到环境变量
az account show。export SUBSCRIPTION="$(az account show --query id --output tsv)"使用
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" }获取托管标识的客户端 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 服务帐户
使用
az aks get-credentials命令连接到 AKS 群集。az aks get-credentials --name "${CLUSTER_NAME}" --resource-group "${RESOURCE_GROUP}"使用
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
在
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) }添加以下代码到
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"]
}
使用 Azure RBAC 授权创建密钥保管库
以下示例演示如何使用 Azure 基于角色的访问控制 (Azure RBAC) 权限模型向 Pod 授予访问密钥保管库的权限。 有关 Azure Key Vault 的 Azure RBAC 权限模型的详细信息,请参阅使用 Azure RBAC 授予应用程序访问 Azure Key Vault 的权限。
使用
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获取密钥保管库资源 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 权限
获取调用方对象 ID,并使用命令将其保存到环境变量
az ad signed-in-user show。export CALLER_OBJECT_ID=$(az ad signed-in-user show --query id -o tsv)将 Azure RBAC Key Vault 机密官角色分配给自己,以便可以使用
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 Key Vault Secrets Officer 角色分配给自己,以便可以在新的密钥保管库中创建机密,并将 Key Vault 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
}
创建和配置机密访问
使用
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\!"获取用户分配的托管标识的主体 ID,并使用命令将其保存到环境变量
az identity show。export IDENTITY_PRINCIPAL_ID=$(az identity show \ --name "${USER_ASSIGNED_IDENTITY_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --query principalId \ --output tsv)使用 将
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使用
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 并测试访问权限
部署一个 Pod 来验证工作负荷标识是否能够访问密钥保管库中的机密。 以下示例使用
ghcr.io/azure/azure-workload-identity/msal-go映像,其中包含使用 Microsoft Entra 工作负荷 ID 从 Azure Key Vault 检索机密的示例应用程序: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使用
Ready命令等待 Pod 处于kubectl wait状态。kubectl wait --namespace ${SERVICE_ACCOUNT_NAMESPACE} --for=condition=Ready pod/sample-workload-identity-key-vault --timeout=120s检查 Pod 中是否通过
SECRET_NAME命令设置了kubectl describe环境变量。kubectl describe pod sample-workload-identity-key-vault | grep "SECRET_NAME:"如果成功,输出应类似于以下示例:
SECRET_NAME: ${KEYVAULT_SECRET_NAME}请确认 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 无法访问机密,则可能需要等待角色分配传播。 有关详细信息,请参阅 Azure RBAC 疑难解答。
在 AKS 群集上禁用 Microsoft Entra 工作负荷 ID
在已启用和配置的 AKS 群集上禁用Microsoft Entra 工作负荷 ID,使用 [az aks update][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
验证部署
使用
az aks get-credentials命令连接到 AKS 群集。az aks get-credentials --name <cluster-name> --resource-group <resource-group>使用
kubectl get pods命令检查验证 Pod 的状态。Pod 达到
Ready状态后,使用kubectl logs命令检查 Pod 日志,验证它是否可以访问密钥保管库机密。kubectl logs workload-identity-test
相关内容
在本文中,您部署了 Kubernetes 集群,并将其配置为使用 Microsoft Entra 工作负载 ID,以便应用程序工作负载可以使用该凭据进行身份验证。 现在,你已准备好部署应用程序并将其配置为将工作负荷标识与最新版 Azure 标识客户端库配合使用。 如果无法重写应用程序以使用最新的客户端库版本,则可以设置应用程序 Pod,以使用托管标识和工作负荷标识作为短期迁移解决方案进行身份验证。