安装在 VM 上的 .NET Framework 控制台应用无法访问 Azure Key Vault

.NET Framework Console app installed on VM can't access Azure Key Vault

我有一个 .NET Framework 4.7.2 控制台应用程序,我正在尝试使用 Azure Key Vault 机密进行配置。

我正在使用 app.config ConfigBuilders 来启用 Key Vault 读取。

<configSections>
    <section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/>
</configSections>
...
<configBuilders>
  <builders>
    <add name="AzureKeyVault" vaultName="key-vault-name" vaultUri="https://key-vault-name.vault.azure.net" type="Microsoft.Configuration.ConfigurationBuilders.AzureKeyVaultConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Azure, Version=1.0.0.0, Culture=neutral"/>
  </builders>
</configBuilders>
...
<connectionStrings configBuilders="AzureKeyVault">
    <add name="DBConnectionString" connectionString="CheckAzureKeyVault"/
</connectionStrings>

这从 Visual Studio 起在 运行 本地有效。当部署到我们的测试 EC2 实例 (Windows Server 2016) 时,当 运行 在有权访问 Key Vault 的服务帐户下 [使用 Active Directory 集成身份验证] 时,它也可以工作。

如果我将相同的编译文件部署到我们的生产 EC2(也是 Windows Server 2016)并且 运行 它在同一个服务帐户下,我会收到以下错误:

Unhandled Exception: System.TypeInitializationException: The type initializer for 'Example.Program' threw an exception. ---> System.Configuration.ConfigurationErrorsException: The configBuilder 'AzureKeyVault' failed during Initialization.: One or more errors occurred. (C:\Example.Program.exe.Config line 21) ---> System.AggregateException: One or more errors occurred. ---> Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/xxxx. Exception Message: Tried the following 4 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/xxx. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: NotFound, Response: <?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>404 - Not Found</title>
 </head>
 <body>
  <h1>404 - Not Found</h1>
 </body>
</html>

Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/xxx. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "C:\xxx\tokenprovider.json"
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/xxx. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. ERROR: Please run 'az login' to setup account.

Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/xxx. Exception Message: Tried to get token using Active Directory Integrated Authentication. Access token could not be acquired. Federated service at https://xxx returned error: See inner exception for detail.Inner Exception :  Response status code does not indicate success: 400 (BadRequest).

   at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.<GetAccessTokenAsyncImpl>d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.KeyVault.KeyVaultCredential.<PreAuthenticate>d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.KeyVault.KeyVaultCredential.<ProcessHttpRequestAsync>d__13.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Microsoft.Azure.KeyVault.KeyVaultClient.<GetSecretsWithHttpMessagesAsync>d__66.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.<GetSecretsAsync>d__50.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Configuration.ConfigurationBuilders.AzureKeyVaultConfigBuilder.<<GetAllKeys>b__17_0>d.MoveNext()
   --- End of inner exception stack trace ---
   at System.AggregateException.Handle(Func`2 predicate)
   at Microsoft.Configuration.ConfigurationBuilders.AzureKeyVaultConfigBuilder.GetAllKeys()
   at Microsoft.Configuration.ConfigurationBuilders.AzureKeyVaultConfigBuilder.Initialize(String name, NameValueCollection config)
   at System.Configuration.ConfigurationBuildersSection.CreateAndInitializeBuilderWithAssert(Type t, ProviderSettings ps)
   --- End of inner exception stack trace ---
   at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)
   at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
   at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
   at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_AppSettings()
   at Example.Program..cctor()
   --- End of inner exception stack trace ---
   at Example.Program.Main(String[] args)

我很难确定为什么它可以在我们的 Dev VM 运行 上以相同的方式运行应用程序,但不能在我们的 Prod VM 上运行。两者都安装了.NET Framework 4.8,两者都可以连接到互联网。

在 Prod VM 上,如果我使用 PowerShell 并首先通过 Azure CLI 登录,我能够成功 运行 应用程序。只有当我尝试 运行 它作为 Prod VM 上的服务帐户时,它才会收到错误。

有什么想法吗?

对于这个特定的错误,我们能够确定它是从自定义域(错误消息 "Federated Service at xxx" 中列出的域)到 AWS ELB 的 CNAME 条目。由于某种原因,应用程序 运行 所在的服务器不喜欢该 CNAME。当我们添加一个将域直接映射到域控制器的主机文件条目时,它起作用了。我们无法解决 CNAME 无法正常播放的根本原因,但我们可以通过主机文件条目来解决它。这可能是我们环境的特定错误,但也许会对某些人有所帮助。