VSTS Build Pipeline:测试无法连接到Azure Key Vault

Hel*_*123 15 c# asp.net azure azure-devops

我正在尝试使用VSTS(现在是Azure DevOps)来执行CI/CD管道.对于我的构建管道,我有一个非常基本的设置,涉及执行恢复,构建,测试和发布步骤.

对于我的测试步骤,我设置了运行两个测试项目 - 一个单元测试项目和一个集成测试项目.我有我的密钥保管库访问策略设置,以提供对我自己和Azure Devops的访问.当我使用visual studio在本地运行我的测试时,当我登录到可以访问azure密钥库的同一帐户时,我可以毫无错误地运行测试.

我的应用程序配置为使用以下设置访问密钥保管库:

 public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((ctx, builder) =>
            {
                var keyVaultEndpoint = GetKeyVaultEndpoint();

                if (!string.IsNullOrEmpty(keyVaultEndpoint))
                {
                    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
                    builder.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                }
            }
        )
            .UseStartup<Startup>();
Run Code Online (Sandbox Code Playgroud)

当我运行构建管道时,我正在使用Hosted VS2017实例来构建我的项目.除了尝试访问密钥库的集成测试失败之外,一切都在运行.我使用以下包:

  • Microsoft.Azure.Services.AppAuthentication - 可以轻松获取Service-to-Azure-Service身份验证方案的访问令牌.
  • Microsoft.Azure.KeyVault - 包含与Key Vault交互的方法.
  • Microsoft.Extensions.Configuration.AzureKeyVault - 包含
    Azure Key Vault的IConfiguration扩展

我按照本教程https://docs.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault来设置密钥保险库并将其集成到我的应用程序中.

我只是试图通过确保单元和集成测试通过来使我的构建工作.我还没有将它部署到应用服务中.单元测试没有任何问题,因为我在嘲笑各种服务.我的集成测试失败,出现以下错误消息.如何获得对密钥保管库的测试访问权限?我是否需要为托管VS2017构建的密钥保管库添加任何特殊访问策略?不知道该怎么办,因为我没有看到任何突出的东西.

建立

下面是错误的堆栈跟踪:

    2018-10-16T00:37:04.6202055Z Test run for D:\a\1\s\SGIntegrationTests\bin\Release\netcoreapp2.1\SGIntegrationTests.dll(.NETCoreApp,Version=v2.1)
    2018-10-16T00:37:05.3640674Z Microsoft (R) Test Execution Command Line Tool Version 15.8.0
    2018-10-16T00:37:05.3641588Z Copyright (c) Microsoft Corporation.  All rights reserved.
    2018-10-16T00:37:05.3641723Z 
    2018-10-16T00:37:06.8873531Z Starting test execution, please wait...
    2018-10-16T00:37:51.9955035Z [xUnit.net 00:00:40.80]     SGIntegrationTests.HomeControllerShould.IndexContentTypeIsTextHtml [FAIL]
    2018-10-16T00:37:52.0883568Z Failed   SGIntegrationTests.HomeControllerShould.IndexContentTypeIsTextHtml
    2018-10-16T00:37:52.0884088Z Error Message:
    2018-10-16T00:37:52.0884378Z  Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException : Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
    2018-10-16T00:37:52.0884737Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response: {"error":"invalid_request","error_description":"Identity not found"}
    2018-10-16T00:37:52.0884899Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Visual Studio Token provider file not found at "C:\Users\VssAdministrator\AppData\Local\.IdentityService\AzureServiceAuth\tokenprovider.json"
    2018-10-16T00:37:52.0885142Z Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/63cd8468-5bc3-4c0a-a6f8-1e314d696937. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. Process took too long to return the token.
    2018-10-16T00:37:52.0885221Z 
    2018-10-16T00:37:52.0885284Z Stack Trace:
    2018-10-16T00:37:52.0885349Z    at Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider.GetAccessTokenAsyncImpl(String authority, String resource, String scope)
    2018-10-16T00:37:52.0885428Z    at Microsoft.Azure.KeyVault.KeyVaultCredential.PostAuthenticate(HttpResponseMessage response)
    2018-10-16T00:37:52.0885502Z    at Microsoft.Azure.KeyVault.KeyVaultCredential.ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    2018-10-16T00:37:52.0886831Z    at Microsoft.Azure.KeyVault.KeyVaultClient.GetSecretsWithHttpMessagesAsync(String vaultBaseUrl, Nullable`1 maxresults, Dictionary`2 customHeaders, CancellationToken cancellationToken)
    2018-10-16T00:37:52.0886887Z    at Microsoft.Azure.KeyVault.KeyVaultClientExtensions.GetSecretsAsync(IKeyVaultClient operations, String vaultBaseUrl, Nullable`1 maxresults, CancellationToken cancellationToken)
    2018-10-16T00:37:52.0886935Z    at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.LoadAsync()
    2018-10-16T00:37:52.0887000Z    at Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationProvider.Load()
    2018-10-16T00:37:52.0887045Z    at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
    2018-10-16T00:37:52.0887090Z    at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
    2018-10-16T00:37:52.0887269Z    at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
    2018-10-16T00:37:52.0887324Z    at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
    2018-10-16T00:37:52.0887371Z    at Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder, IFeatureCollection featureCollection)
    2018-10-16T00:37:52.0887433Z    at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateServer(IWebHostBuilder builder)
    2018-10-16T00:37:52.0887477Z    at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
    2018-10-16T00:37:52.0887525Z    at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
Run Code Online (Sandbox Code Playgroud)

更新

我发现此问题只有1个相关帖子:https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview.但该帖子与将应用程序部署到azure插槽有关.我只是想在构建管道中构建我的应用程序.

我仍在尝试解决此问题,并且不确定提供所需访问权限的最佳方式是什么.


更新2

我还没有找到解决方案.我迷失了如何让我的管道运行我的测试没有问题.我看到了发布管道,你也可以选择运行测试.但这些似乎采取.dll文件和我的构建管道drop文件只有web应用程序(我没有看到任何测试项目发布的drop文件).不确定是否有可能.


更新3

我设法通过使用此处提供的最后一个选项使其工作:https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support

我尝试了使用证书的其他方法,但无论何时在连接字符串中提供{CurrentUser},构建管道都会失败.它适用于我的本地计算机,但不适用于构建管道.

要让它发挥作用,我必须做三件事:

  • 登录Azure.在Azure AD中设置新的应用程序注册
  • 在新的AD应用注册中,创建新的客户端密钥 在此输入图像描述
  • 为您的密钥保管库提供新的AD App访问权限.进入您的密钥保管库访问策略,并添加您在AD中创建的应用程序,并具有对您机密的读取权限. 在此输入图像描述

  • 修改了我在Program.cs文件中对AzureServiceTokenProvier()的调用,如下所示:

     var azureServiceTokenProvider = new AzureServiceTokenProvider("connectionString={your key vault endpoint};RunAs=App;AppId={your app id that you setup in Azure AD};TenantId={your azure subscription};AppKey={your client secret key}")
    
    Run Code Online (Sandbox Code Playgroud)

请注意,您的客户端密钥必须正确格式化.应用程序注册(预览)生成随机密钥.有时此键在连接字符串中不起作用(因错误格式化而引发错误).尝试在非预览版应用注册中生成您自己的密钥或生成新密钥,然后重试.

之后,我能够在我的构建管道中成功运行集成测试,并在Azure中创建一个发布到我的Web应用程序.我对这种方法不满意,因为虽然它有效,但它在代码本身中暴露了一个秘密值.由于上述方法,不需要打开管理服务标识.在这方面,我觉得这是非常糟糕的.

必须有一个比这更好的方法.一种选择是不在构建管道中运行集成测试.不确定这是否是正确的方法.我仍然希望有人能够提供更好的方法或解释我的方法是否可以使用.

Sae*_*ini 9

使用Azure CLI 管道任务成功运行需要 KeyVault 机密的集成测试,而不会在源代码控制中公开任何机密:

  1. 在 Azure DevOps 项目中创建 服务主体服务连接

  2. 授予主体获取列出Azure 中 Vault 的权限。

  3. 在 Azure CLI 任务中运行集成测试:

    - task: AzureCLI@1
      inputs:
        azureSubscription: 'Your Service Connection Name'
        scriptLocation: 'inlineScript'
        inlineScript: 'dotnet test --configuration $(buildConfiguration) --logger trx'
    
    Run Code Online (Sandbox Code Playgroud)

    这是有效的,因为测试将在 azure cli 的上下文中运行,这是AzureServiceTokenProvider 尝试从失败之前获取令牌的地方。Azure CLI 处理身份验证并在任务完成后进行清理。


Eri*_*ono 5

您不应在Azure DevOps Pipelines构建内对Azure KeyVault进行身份验证的集成测试,因为您使用的是Azure DevOps默认托管代理。

默认情况下,Azure DevOps Pipelines使用的是基本的默认托管代理,并且这些托管代理无法通过Azure订阅进行访问。这些不足为奇,因为这些托管代理是满足所有常见构建需求的通用代理,包括构建/编译,运行单元测试,获取测试覆盖率,并且所有这些任务都没有其他附加功能,例如具有ActiveDirectory,数据库和其他功能。对其他方的实际身份验证/请求,例如对任何Azure Keyvault的身份验证。因此,默认情况下,这些代理未在您的Azure订阅中注册。

如果要针对这些特殊需求进行成功的集成测试,则必须为Azure DevOps Pipelines构建和发布创建自己的代理。因此,除了创建自己的代理并将Azure DevOps配置为使用自己的代理外,没有其他方法可以强制Azure DevOps默认代理运行KeyVault身份验证测试。

要创建自己的代理,请查阅Microsoft的以下文档:

https://docs.microsoft.com/zh-cn/azure/devops/pipelines/agents/agents?view=vsts#install

更新2018年10月29日

为了更清楚,我也答复您的“ Update 3”解决方法。当Microsoft更新Azure DevOps的默认托管代理时,无法保证您的解决方法将正常工作。因此,我还需要补充一点:进行集成测试而不依赖Azure DevOps Pipeline构建领域之外的另一方(例如连接数据库服务器或在其中使用外部身份验证(甚至在Azure KeyVault上)),这不是一个好习惯CI,尤其是在使用Microsoft的默认托管代理的情况下。

由于无效的身份验证配置,不仅会容易出错,而且无法保证默认托管代理上的进一步更新将确保您的第三方逻辑测试能够正常工作。