发生套接字异常,Azure Key Vault秘密访问间歇性失败

时间:2019-02-05 20:14:59

标签: 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.3 NuGet包,并使用KeyVaultClient.GetSecretAsync()访问秘密。所有资源都位于同一Azure区域。

在大多数情况下,此方法效果很好,并且在90%的时间内,它以毫秒为单位返回秘密。

Working Key Vault Access

但是,偶尔访问该Vault的调用失败。这本身并没有表现为SDK引发的异常,但是Web应用程序挂起。最终-通常在1分钟左右,但有时甚至更长的时间-秘密被返回,一切都很好。这是因为SDK使用了重试模式,它将继续尝试获取机密。

查看AppService的应用程序见解我可以看到,SDK生成的GET请求从Key Vault获得HTTP 500响应,并抛出SocketException,结果代码为ConnectFailure。

Exception

例外是:

Exception

查看遥测并逐步执行代码,没有任何共性或明显原因。这似乎是完全随机的。

最重要的是,Azure托管的AppService有时 不能使用最新的框架和SDK版本连接到同一数据中心中的Azure托管的Key Vault。

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

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

1 个答案:

答案 0 :(得分: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;
        }
    }
}

这还没准备好投入生产-您需要修改它并实现GetKeyVaultClient方法以实际返回您的KeyVaultClient对象,并且GetSecret方法还应该清理检索到的键名。

在我们的DI注册中心中,我们进行了以下设置以使用单例:

For<IAppSettings>().Use<MyAppSettings>().Singleton();

这两个更改似乎对我们来说效果很好,而且我们暂时还没有遇到任何问题。