简体   繁体   English

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

[英]Azure Key Vault secret access intermittently failing to connect with socket exception

I have an MVC 5 web application running on .NET 4.7.2 and hosted in an Azure AppService, that uses Azure Key Vault to hold secrets.我有一个在 .NET 4.7.2 上运行并托管在 Azure AppService 中的 MVC 5 Web 应用程序,它使用 Azure Key Vault 来保存机密。 The project uses the Microsoft.Azure.KeyVault 3.0.3 NuGet package and the secrets are accessed using the KeyVaultClient and .GetSecretAsync() .该项目使用Microsoft.Azure.KeyVault 3.0.3 NuGet 包,并且使用KeyVaultClient.GetSecretAsync()访问机密。 All resources are located in the same Azure region.所有资源都位于同一个 Azure 区域。

For the most part this works very well, and for about 90% of the time it returns the secret in milliseconds.在大多数情况下,这非常有效,并且在大约 90% 的时间里,它以毫秒为单位返回秘密。

工作 Key Vault 访问

But every now and then the call to access the Key Vault fails.但访问 Key Vault 的调用时不时会失败。 This doesn't manifest itself as an exception thrown by the SDK, but the web app hangs.这不会表现为 SDK 抛出的异常,而是 Web 应用程序挂起。 Eventually - and normally in around 1 minute but sometimes longer - the secret is returned and all is fine again.最终——通常在 1 分钟左右,但有时会更长——秘密被返回,一切又好起来了。 This is because the SDK uses a retry pattern, which will keep trying to get the secret.这是因为 SDK 使用重试模式,它将不断尝试获取秘密。

Looking at Application Insights for the AppService I can see that the GET request generated by the SDK gets an HTTP 500 response from the Key Vault and a SocketException is thrown, with a result code of ConnectFailure.查看 AppService 的 Application Insights,我可以看到 SDK 生成的 GET 请求从 Key Vault 获取 HTTP 500 响应并抛出 SocketException,结果代码为 ConnectFailure。

例外

The exception is:例外是:

例外

Looking at the telemetry and stepping through the code there is no element of commonality or obvious cause.查看遥测和单步执行代码,没有共同点或明显原因。 It seems to be entirely random.这似乎是完全随机的。

The bottom line is the Azure hosted AppService sometimes cannot connect to an Azure hosted Key Vault in the same datacentre, using the latest framework and SDK version.底线是 Azure 托管的 AppService有时无法使用最新的框架和 SDK 版本连接到同一数据中心中的 Azure 托管的 Key Vault。

Has anyone else seen this or have any idea?有没有其他人看到这个或有任何想法? I've searched around and found a few instances of people experiencing the same issue, but nobody has a cause or solution.我四处搜索,发现有几个人遇到了同样的问题,但没有人有原因或解决方案。

EDIT (1): I have now tried spinning up a new Key Vault in a different region entirely, and the problem remains exactly the same.编辑(1):我现在尝试在完全不同的区域启动一个新的 Key Vault,问题仍然完全相同。

We experienced the same behavior on our project, where KeyVault would be fast and reliable most of the time, and then intermittently stop responding or take a very long time to return once in a while with no obvious reason to explain why. 我们在项目中遇到了相同的行为,其中KeyVault在大多数时间内都是快速可靠的,然后间歇性地停止响应或者花费很长时间偶尔返回,没有明显的理由解释原因。 This occurred in all tiers of our application, from the API, to Azure Functions, to command line tools. 这发生在我们的应用程序的所有层中,从API,Azure函数到命令行工具。

Eventually, we had to work around this by caching secrets in memory to avoid hitting the KeyVault too often, where our AppSettings class would cache these internally. 最终,我们不得不通过在内存中缓存秘密来解决这个问题,以避免过于频繁地访问KeyVault,我们的AppSettings类会在内部缓存它们。 In addition to this, we also configured our DI container to treat this class as a singleton. 除此之外,我们还配置了DI容器,将此类视为单例。

Here is a very simplified example: 这是一个非常简单的例子:

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;
        }
    }
}

This isn't production ready - you'll need to modify this and implement the GetKeyVaultClient method to actually return your KeyVaultClient object, and also the GetSecret method should sanitize the key name being retrieved. 这不是生产准备 - 您需要修改它并实现GetKeyVaultClient方法以实际返回KeyVaultClient对象,并且GetSecret方法还应清理正在检索的密钥名称。

In our DI registry, we had this setup to use a singleton like this: 在我们的DI注册表中,我们有这样的设置使用这样的单例:

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

These two changes seemed to work well for us, and we haven't had any issues with this for a while now. 这两个变化对我们来说似乎很有效,而且我们暂时没有遇到任何问题。

Another option is to deploy the secrets from keyvault to your app service application as app settings in your deployment pipeline.另一种选择是将密钥库中的机密作为部署管道中的应用设置部署到您的应用服务应用程序。

Pros:优点:

  • Keep the secrets out of source control将秘密置于源代码控制之外
  • Remove the runtime dependency on keyvault删除对密钥库的运行时依赖
  • Faster reliable local access to the secrets更快可靠的本地访问机密

Cons:缺点:

  • Updating the secrets requires a redeploy更新机密需要重新部署

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM