首页 > 解决方案 > 在 Linux docker 容器中运行 Asp.Net Core 3.1 的 Azure 应用服务是否支持用户分配的托管标识?

问题描述

我有一个 Asp.Net Core 3.1 应用程序,它使用以下代码从 Key Vault 读取其配置:

var keyVaultEndpoint = builtConfig["ProductKeyVaultUri"];
if (!string.IsNullOrEmpty(keyVaultEndpoint))
{
    var azureServiceTokenProvider = new AzureServiceTokenProvider();
    var keyVaultClient = new KeyVaultClient(
        new KeyVaultClient.AuthenticationCallback(
            azureServiceTokenProvider.KeyVaultTokenCallback));
    config.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient,
        new DefaultKeyVaultSecretManager());
}

它使用迄今为止最新版本的Microsoft.Azure.Services.AppAuthentication软件包 - 1.4.0

该应用程序部署到具有系统托管标识(简称 MI)的 Azure 应用服务,该标识可以从相关的 Key Vault 中读取机密。有用。

实际上,当我从 Key Vault 访问策略中删除系统 MI 并重新启动应用服务时,我得到了以下信息:

Unhandled exception. Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Access denied. Caller was not found on any access policy.
Caller: appid=6f215b10-33a1-4e5d-b3b7-20e8f3d3b587;oid=3d6af26c-af56-4cef-a832-41c2303a8cbe;numgroups=0;iss=https://sts.windows.net/2...b/
Vault: a...v;location=eastus2
   at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
   at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
   at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
   at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
   at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
   at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
   at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at Gateway.Program.Main(String[] args)
/opt/startup/init_container.sh: line 20:    10 Aborted                 (core dumped) dotnet Gateway.dll

(我清理了tenantId 和密钥保管库名称)

它为我们提供了与系统 MI 匹配的 AppId (6f215b10-33a1-4e5d-b3b7-20e8f3d3b587) 和 ObjectId (3d6af26c-af56-4cef-a832-41c2303a8cbe)。

到目前为止,一切都很好。

现在,我将系统 MI 替换为用户分配的 MI,它可以访问同一 KV 中的机密。但是,重新启动应用服务不会产生任何好处。Web 应用程序无法从 Key Vault 读取机密,这会中止容器的启动。这是 docker logs 告诉我的:

Unhandled exception. Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. 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://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. Received a non-retryable error. MSI ResponseCode: BadRequest, Response: {"statusCode":400,"message":"Unable to load requested managed identity.","correlationId":"1b0ee635-0805-4438-8ae8-747e9f6dd7c2"}
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Environment variable LOCALAPPDATA not set.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/2...b. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. /bin/bash: az: No such file or directory


   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAuthResultAsyncImpl(String resource, String authority, CancellationToken cancellationToken)
   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.<get_KeyVaultTokenCallback>b__8_0(String authority, String resource, String scope)
   at Microsoft.Azure.KeyVault.KeyVaultCredential.PostAuthenticate(HttpResponseMessage response)
   at Microsoft.Azure.KeyVault.KeyVaultCredential.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
   at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
   at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
   at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
   at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
   at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
   at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at Gateway.Program.Main(String[] args)
/opt/startup/init_container.sh: line 20:    10 Aborted                 (core dumped) dotnet Gateway.dll

从中我得出结论,必须明确告知 docker 用户分配的 MI 的名称。这有点道理,因为应用服务可能有许多用户分配的 MI,但只有一个系统 MI。

所以,问题是——我们可以在这种情况下使用用户分配的 MI 吗?

标签: azuredockerauthenticationasp.net-coreazure-keyvault

解决方案


问题可能是您没有告诉AzureServiceTokenProvider用户分配的 MI 的 ID。

在文档中,您可以看到连接字符串语法的示例:https ://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support 。

对于您的情况,请指定一个连接字符串,例如:

RunAs=App;AppId={用户分配身份的ClientId}

并将其用作令牌提供者的构造函数参数。默认情况下,提供者只尝试系统分配的 MI。


推荐阅读