使用用户分配的托管标识进行 Azure 服务总线身份验证

Azure Service Bus authentication with User Assigned Managed Identities

我们需要 运行 个应用程序从 Azure 应用服务和 Azure Functions 访问 Azure 服务总线 (ASB)。我们需要使用用户分配的身份进行身份验证。我们编写以下代码,适用于系统分配的身份,但不适用于用户分配的身份:

var tokenProvider = TokenProvider.CreateManagedIdentityTokenProvider();
var managementClient = new ManagementClient(serviceBusEndpoint, tokenProvider);

if(await managementClient.QueueExistsAsync(queueName))
{
    return new OkObjectResult($"Queue with name {queueName} exists.");
}
else
{
    return new OkObjectResult($"Queue with name {queueName} doesn't exist.");
}

抛出此错误:

Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response: {"StatusCode":400,"Message":"No MSI found for specified ClientId/ResourceId.","CorrelationId":"dd2bcf6c-6f1d-489e-b178-ca6007502841"}
Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "D:\local\LocalAppData\.IdentityService\AzureServiceAuth\tokenprovider.json"
Parameters: Connection String: [No connection string specified], Resource: https://servicebus.azure.net/, Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. 'az' is not recognized as an internal or external command,
operable program or batch file.


   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsyncImpl(String authority, String resource, String scope)
   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsync(String resource, String tenantId)
   at Microsoft.Azure.ServiceBus.Primitives.ManagedIdentityTokenProvider.GetTokenAsync(String appliesTo, TimeSpan timeout)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetToken(String requestUri)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.SendHttpRequest(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetEntity(String path, String query, Boolean enrich, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.GetQueueAsync(String queuePath, CancellationToken cancellationToken)
   at Microsoft.Azure.ServiceBus.Management.ManagementClient.QueueExistsAsync(String queuePath, CancellationToken cancellationToken)

所以核心错误是No MSI found for specified ClientId/ResourceId.。看起来我们需要指定客户端 ID。然后我们发现 https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/identity/Azure.IdentityManagedIdentityCredential 我们可以在其中指定客户端 ID,但还没有想出如何将它用于 ASB。

其他选项是使用 HTTP GET 请求 MSI_ENDPOINT,如此处所述 https://docs.microsoft.com/en-us/azure/app-service/overview-managed-identity?context=azure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fcontext%2Fmsi-context&tabs=dotnet#obtaining-tokens-for-azure-resources,但可能存在我们错过的真实工作 .net 库。

我在 Azure SDK 团队。我们正在此处统一所有 Azure SDK:https://aka.ms/azsdkpackages,但还没有新的服务总线 SDK。

我没有尝试使用 TokenProvider API 进行用户分配的身份,但我知道它适用于新的 DefaultAzureCredential 对象,它将在您的环境中搜索凭证,并自动获取托管身份端点。您可以在这里阅读更多相关信息:https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet

但是您不能直接使用 DefaultAzureCredential,因为 ServiceBus 接受 ITokenProvider。

同时,您可以使用我创建的这个适配器 - 在我们拥有服务总线 SDK 之前,这只是权宜之计。

  1. 将 Azure.Identity 和 Azure.Core nuget 包添加到您的项目中。

  2. 将此文件复制到您的项目中: https://github.com/jongio/azidext/blob/master/net/JonGallant.Azure.Identity.Extensions/DefaultAzureServiceBusCredential.cs

  3. 这是一个示例用法 https://github.com/jongio/azidext/blob/master/net/JonGallant.Azure.Identity.Extensions.Tests/ServiceBus/ServiceBusTests.cs

您可以使用以下环境变量设置客户端 ID、机密和租户 ID:

AZURE_CLIENT_ID

AZURE_CLIENT_SECRET

AZURE_TENANT_ID

如果您必须使用 TokenProvider 而不想使用权宜之计,请告诉我,我可以进一步研究。

TokenProvider.CreateManagedIdentityTokenProvider 依赖于 Microsoft.Azure.Services.AppAuthentication 来实现托管身份。

Microsoft.Azure.Services.AppAuthentication 的 1.2.1 版支持用户分配的托管标识。可以找到文档 here

因此,您必须两件事才能使用您已有的代码进行此操作:

1.Update版本Microsoft.Azure.Services.AppAuthentication到最新

2.Set AzureServicesAuthConnectionString在AppService的App设置中改为RunAs=App;AppId={ClientId of user-assigned identity}。例如RunAs=App;AppId=587f16c8-81ed-41c7-b19a-9ded0dbe2ca2

完成这两个步骤后,您的代码应该使用用户分配的托管标识。我使用应用服务对此进行了尝试,它对我来说效果很好。

从今天开始,我们可以使用以下代码分配给用户。

var managedCredential = new ManagedIdentityCredential(userAssignedIdentityAppId);
var accessToken = await _managedCredential.GetTokenAsync(
                        new TokenRequestContext(
                            new[] { "https://servicebus.azure.net" })).ConfigureAwait(false);

它也适用于 "https://servicebus.azure.net/.default"