Azure Key Vault 秘密访问间歇性地无法连接,出现套接字异常

Ira*_*ney 5 c# asp.net-mvc azure azure-application-insights azure-keyvault

我有一个在 .NET 4.7.2 上运行并托管在 Azure AppService 中的 MVC 5 Web 应用程序,它使用 Azure Key Vault 来保存机密。该项目使用Microsoft.Azure.KeyVault 3.0.3NuGet 包,并且使用KeyVaultClient和访问机密.GetSecretAsync()。所有资源都位于同一个 Azure 区域。

在大多数情况下,这非常有效,并且在大约 90% 的时间里,它以毫秒为单位返回秘密。

工作 Key Vault 访问

但访问 Key Vault 的调用时不时会失败。这不会表现为 SDK 抛出的异常,而是 Web 应用程序挂起。最终——通常在 1 分钟左右,但有时会更长——秘密被返回,一切又好起来了。这是因为 SDK 使用重试模式,它将不断尝试获取秘密。

查看 AppService 的 Application Insights,我可以看到 SDK 生成的 GET 请求从 Key Vault 获取 HTTP 500 响应并抛出 SocketException,结果代码为 ConnectFailure。

例外

例外是:

例外

查看遥测和单步执行代码,没有共同点或明显原因。这似乎是完全随机的。

底线是 Azure 托管的 AppService有时无法使用最新的框架和 SDK 版本连接到同一数据中心中的 Azure 托管的 Key Vault。

有没有其他人看到这个或有任何想法?我四处搜索,发现有几个人遇到了同样的问题,但没有人有原因或解决方案。

编辑(1):我现在尝试在完全不同的区域启动一个新的 Key Vault,问题仍然完全相同。

Mun*_*Mun 4

我们在项目中经历了同样的行为,KeyVault 在大多数情况下都快速可靠,然后间歇性地停止响应或偶尔需要很长时间才能返回,并且没有明显的原因来解释原因。这种情况发生在我们应用程序的所有层中,从 API 到 Azure Functions,再到命令行工具。

最终,我们必须通过在内存中缓存秘密来解决这个问题,以避免过于频繁地访问 KeyVault,我们的 AppSettings 类将在内部缓存这些秘密。除此之外,我们还配置了 DI 容器以将此类视为单例。

这是一个非常简单的例子:

public class MyAppSettings : IAppSettings
{
    private readonly ObjectCache _cache = MemoryCache.Default;
    private readonly object _lock = new Object();
    private KeyValueClient _kvClient;

    public string MySecretValue => GetSecret("MySecretValue");

    private KeyValueClient GetKeyVaultClient()
    {
        // Initialize _kvClient if required

        return _kvClient;
    }

    private string GetSecret(string name)
    {
        lock (_lock)
        {
            if (_cache.Contains(key))
                return (string) _cache.Get(key);

            // Sanitize name if required, remove reserved chars

            // Construct path
            var path = "...";

            // Get value from KV

            var kvClient = GetKeyVaultClient();
            Task<SecretBundle> task = Task.Run(async() => await kvClient.GetSecretAsync(path));

            var value = task.Result;

            // Cache it
            _cache.Set(name, value, DateTime.UtcNow.AddHours(1));

            return value;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这还没有准备好用于生产 - 您需要修改它并实现该GetKeyVaultClient方法以实际返回您的 KeyVaultClient 对象,并且该GetSecret方法还应该清理正在检索的密钥名称。

在我们的 DI 注册表中,我们设置了使用如下所示的单例:

For<IAppSettings>().Use<MyAppSettings>().Singleton();
Run Code Online (Sandbox Code Playgroud)

这两项更改似乎对我们来说效果很好,而且我们已经有一段时间没有遇到任何问题了。