使用用户分配托管身份编程认证AKS与Azure AD报未授权错误排查
问题根因
运行代码返回401未授权是四个配置错误共同导致的:
- 调用IMDS元数据接口获取AKS访问令牌时,
resource参数错误添加了.default后缀:你调用的http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01是Azure AD v1.0令牌接口,仅支持传入纯资源URI/应用ID作为resource值,.default是Azure AD v2.0接口的scope语法,v1接口无法识别,最终返回的令牌受众(aud)和AKS集群要求完全不匹配。 - AKS令牌受众配置与集群能力不匹配:你传入的
6dae42f8-4368-4678-94ff-3960e28e3630是老版本未开启Azure RBAC的AKS全球版服务端应用ID,如果你集群开启了Azure RBAC for AKS,合法受众应为https://aks.azure.com;如果是Azure中国区AKS,对应受众还需要替换为中国区专属值。 - 手动修改kubeconfig authProvider字段的逻辑存在兼容问题:部分版本的C# Kubernetes客户端会在发起请求前自动触发内置的azure auth provider刷新令牌逻辑,直接覆盖你手动写入的
access-token值,最终还是用错误配置的令牌发起请求。 - 权限继承逻辑认知偏差:你给用户分配托管身份授予的资源组/订阅级Owner角色属于Azure管理平面权限,不会自动继承为AKS集群的数据平面访问权限,需要单独在集群作用域配置对应RBAC规则。
排查修复步骤
- 第一步:校验RBAC权限配置
先在VM本地通过Azure CLI验证身份权限是否正常:- 执行
az login --identity -u <你的用户分配托管身份clientID>登录 - 执行
az aks get-credentials -g <AKS资源组名> -n <AKS集群名>拉取普通用户kubeconfig - 执行
kubectl get ns验证是否能正常访问集群,如果这一步也报401,说明是RBAC配置问题,和代码无关:- 若集群开启Azure RBAC for AKS,需要在AKS集群资源作用域给托管身份授予
Azure Kubernetes Service Cluster User或者更高的数据平面角色 - 若集群使用原生Kubernetes RBAC,需要在集群内创建对应ClusterRoleBinding,将权限绑定到托管身份对应的Azure AD服务主体
- 若集群开启Azure RBAC for AKS,需要在AKS集群资源作用域给托管身份授予
- 执行
- 第二步:修正令牌获取逻辑
统一使用DefaultAzureCredential获取两类令牌,不要手动调用IMDS接口:- 访问Azure管理平面拉取kubeconfig的令牌scope固定为
https://management.azure.com/.default - 访问AKS数据平面的令牌scope根据集群配置选择:开启Azure RBAC的全球版集群用
https://aks.azure.com/.default,未开启Azure RBAC的老版本全球版集群用6dae42f8-4368-4678-94ff-3960e28e3630/.default
- 访问Azure管理平面拉取kubeconfig的令牌scope固定为
- 第三步:绕开Kubernetes客户端内置的Azure认证逻辑
不要手动修改kubeconfig里的authProvider字段,通过自定义HTTP请求管道的方式自动注入有效令牌,避免内置逻辑覆盖令牌配置。
修复后可运行代码示例
using System.Net.Http.Headers; using Azure.Core; using Azure.Identity; using k8s; using k8s.Models; using Microsoft.Azure.Management.Fluent; using Microsoft.Azure.Management.ResourceManager.Fluent; using Microsoft.Rest; // 基础配置项 string userAssignedClientId = "0f2a4a25-e37f-4aba-942a-5c58f39eb136"; string subscriptionId = "替换为你的订阅ID"; string resourceGroupName = "替换为AKS所在资源组名"; string aksClusterName = "替换为你的AKS集群名"; // 按集群实际配置替换AKS数据平面受众: // 1. 开启Azure RBAC的全球版AKS: https://aks.azure.com // 2. 未开Azure RBAC的老版本全球版AKS: 6dae42f8-4368-4678-94ff-3960e28e3630 // 3. 开启Azure RBAC的中国区AKS: https://aks.azure.cn string aksAudiance = "https://aks.azure.com"; string armScope = "https://management.azure.com/.default"; // 初始化托管身份凭证 var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId }); // 获取ARM令牌,拉取用户维度kubeconfig var armToken = credential.GetToken(new TokenRequestContext(new[] { armScope })).Token; var armTokenCred = new TokenCredentials(armToken); var azureCred = new Microsoft.Azure.Management.ResourceManager.Fluent.Authentication.AzureCredentials( armTokenCred, armTokenCred, null, AzureEnvironment.AzureGlobalCloud); var azureClient = Azure.Authenticate(azureCred).WithSubscription(subscriptionId); var kubeConfigBytes = azureClient.KubernetesClusters.GetUserKubeConfigContents(resourceGroupName, aksClusterName); // 加载kubeconfig,清空内置认证配置避免冲突 var kubeConfigRaw = KubernetesClientConfiguration.LoadKubeConfig(new MemoryStream(kubeConfigBytes)); var k8sConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubeConfigRaw); k8sConfig.AccessToken = null; k8sConfig.Username = null; k8sConfig.Password = null; // 注入自定义令牌处理器,自动刷新AKS访问令牌 k8sConfig.CreateHttpClientHandler = () => { var rootHandler = new HttpClientHandler(); return new AksTokenInjectHandler(credential, aksAudiance) { InnerHandler = rootHandler }; }; // 初始化客户端执行测试操作 var k8sClient = new Kubernetes(k8sConfig); var nsList = k8sClient.CoreV1.ListNamespace(); foreach (var ns in nsList.Items) { Console.WriteLine($"命名空间: {ns.Metadata.Name}"); var podList = k8sClient.CoreV1.ListNamespacedPod(ns.Metadata.Name); foreach (var pod in podList.Items) { Console.WriteLine($" Pod: {pod.Metadata.Name}"); } } /// <summary> /// AKS令牌自动注入处理器 /// </summary> internal class AksTokenInjectHandler : DelegatingHandler { private readonly TokenCredential _tokenCredential; private readonly string[] _aksScopes; public AksTokenInjectHandler(TokenCredential credential, string aksAudience) { _tokenCredential = credential; _aksScopes = new[] { $"{aksAudience}/.default" }; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var tokenRes = await _tokenCredential.GetTokenAsync(new TokenRequestContext(_aksScopes), cancellationToken); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenRes.Token); return await base.SendAsync(request, cancellationToken); } }
额外校验点
拿到AKS访问令牌后,可以解码JWT的payload部分,确认aud字段值和你配置的AKS受众完全一致,如果不一致说明scope配置仍然错误。
内容的提问来源于stack exchange,提问作者Ching Kuo
相关产品推荐
相关产品推荐

