简体   繁体   English

VSTS 构建管道:测试连接到 Azure Key Vault 失败

[英]VSTS Build Pipeline: Test fails connecting to Azure Key Vault

I am trying to use VSTS (now Azure DevOps) to do a CI/CD pipeline.我正在尝试使用 VSTS(现在是 Azure DevOps)来执行 CI/CD 管道。 For my build pipeline, I have a very basic setup involving doing a restore, build, test, and publish steps.对于我的构建管道,我有一个非常基本的设置,包括执行还原、构建、测试和发布步骤。

For my test step, I have it setup to run two test projects - one unit test project and one integration test project.对于我的测试步骤,我将它设置为运行两个测试项目 - 一个单元测试项目和一个集成测试项目。 I have my Key Vault access policy setup to provide access to both myself and Azure Devops.我设置了 Key Vault 访问策略以提供对我自己和 Azure Devops 的访问。 When I run my tests locally using visual studio, as I am logged into the same account which has access to azure key vault, I can run the tests without any errors.当我使用 Visual Studio 在本地运行我的测试时,因为我登录到可以访问 Azure Key Vault 的同一个帐户,我可以运行测试而不会出现任何错误。

My application is configured to access key vault using below setup:我的应用程序配置为使用以下设置访问密钥保管库:

 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>();

When I run the build pipeline, I am using a Hosted VS2017 instance to build my project.当我运行构建管道时,我使用托管 VS2017 实例来构建我的项目。 Everything is running except the integration tests which try and access the key vault fail.除了尝试访问密钥保管库的集成测试失败之外,一切都在运行。 I am using the following packages:我正在使用以下软件包:

  • Microsoft.Azure.Services.AppAuthentication - makes it easy to fetch access tokens for Service-to-Azure-Service authentication scenarios. Microsoft.Azure.Services.AppAuthentication - 可以轻松获取服务到 Azure 服务身份验证方案的访问令牌。
  • Microsoft.Azure.KeyVault - contains methods for interacting with Key Vault. Microsoft.Azure.KeyVault - 包含与 Key Vault 交互的方法。
  • Microsoft.Extensions.Configuration.AzureKeyVault - contains Microsoft.Extensions.Configuration.AzureKeyVault - 包含
    IConfiguration extensions for Azure Key Vault Azure Key Vault 的 IConfiguration 扩展

I followed this tutorial https://docs.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault to setup the key vault and integrate it into my app.我按照本教程https://docs.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault设置密钥保管库并将其集成到我的应用程序中。

I am merely trying to get my build to work by making sure both the unit and integration tests pass.我只是想通过确保单元和集成测试都通过来让我的构建工作。 I am not deploying it to an app service yet.我还没有将它部署到应用服务。 The unit tests run without any issues as I am mocking the various services.单元测试运行没有任何问题,因为我正在嘲笑各种服务。 My integration test is failing with below error messages.我的集成测试失败并显示以下错误消息。 How do I get my test access to the key vault?如何让我的测试访问密钥保管库? Do I need to add any special access policies to my key vault for the hosted VS2017 build?我是否需要为托管的 VS2017 版本向我的密钥保管库添加任何特殊访问策略? Not sure what to do as I don't see anything that stands out.不知道该怎么做,因为我没有看到任何突出的东西。

建造

Below is the stack trace for the error:以下是错误的堆栈跟踪:

    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)

Update更新

I have found only 1 related post to this issue: https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview .我只找到了 1 个与此问题相关的帖子: https : //social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview . But the post is related to deploying an application into an azure slot.但该帖子与将应用程序部署到 azure 插槽有关。 I am merely trying to build my application in a build pipeline.我只是想在构建管道中构建我的应用程序。

I am still trying to solve this issue and am not sure what the best way to provide the required access is.我仍在尝试解决此问题,但不确定提供所需访问权限的最佳方法是什么。


Update 2更新 2

I have still not found a solution for this.我仍然没有找到解决方案。 I am lost on how to get my pipeline to run my test without issues.我不知道如何让我的管道毫无问题地运行我的测试。 I saw that the release pipeline you have the options of running tests too.我看到发布管道也可以选择运行测试。 But these seem to take .dll files and my build pipeline drop file only has the web app (I don't see any of the test projects published drop file).但是这些似乎需要 .dll 文件,而我的构建管道放置文件只有 Web 应用程序(我没有看到任何已发布的测试项目放置文件)。 Not sure if that is even a possibility.不确定这是否有可能。


Update 3更新 3

I managed to get it to work by using the last option provided here: https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support我设法使用此处提供的最后一个选项使其工作: https : //docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support

I tried the other ways of using a certificate but anytime {CurrentUser} is provided in a connection string, the build pipeline fails.我尝试了其他使用证书的方法,但只要在连接字符串中提供 {CurrentUser},构建管道就会失败。 It works on my local machine but not in the build pipeline.它适用于我的本地机器,但不适用于构建管道。

To get it to work, I had to do three things:为了让它工作,我必须做三件事:

  • Log in to Azure.登录到 Azure。 Setup a new app registration in Azure AD在 Azure AD 中设置新的应用注册
  • In your new AD app registration, create a new client secret在您的新 AD 应用注册中,创建一个新的客户端密钥在此处输入图片说明
  • Provide your new AD App access to your key vault.为您的新 AD 应用程序提供对密钥保管库的访问权限。 Go into your key vault access policies and add the app that you created in your AD with read access to your secrets.进入您的 Key Vault 访问策略并添加您在 AD 中创建的具有对您的机密的读取访问权限的应用程序。 在此处输入图片说明

  • Modified my call to AzureServiceTokenProvier() in my Program.cs file as follows:修改了我在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}")

Note that your client secret has to be formatted correctly.请注意,您的客户端密码必须正确格式化。 The app registrations (preview) generates a random secret key.应用程序注册(预览)会生成一个随机密钥。 Sometimes this key does not work in the connection string (throws an error as incorrectly formatted).有时此键在连接字符串中不起作用(由于格式不正确而引发错误)。 Either try generating your own key in the non-preview version of app registration or generate a new key and try again.尝试在应用注册的非预览版中生成您自己的密钥,或者生成一个新密钥并重试。

After that I was able to run my integration test in my build pipeline successfully and create a release to my web app in Azure.之后,我能够在我的构建管道中成功运行我的集成测试,并在 Azure 中为我的 Web 应用程序创建一个版本。 I'm not satisfied with this approach because although it works, its exposing a secret value in the code itself.我对这种方法并不满意,因为虽然它有效,但它在代码本身中暴露了一个秘密值。 Manages service identity does not need to be turned on due to above approach.由于上述方法,不需要开启管理服务标识。 I feel that this is extremely bad in that regard.我觉得这在这方面是非常糟糕的。

There has to be a better way than this.必须有比这更好的方法。 One option is not to run the integration test in the build pipeline.一种选择是不在构建管道中运行集成测试。 Not sure if this is the correct approach.不确定这是否是正确的方法。 I'm still hoping someone will be able to provide a better approach to this or explain if my approach is okay to use.我仍然希望有人能够为此提供更好的方法或解释我的方法是否可以使用。

Use the Azure CLI pipeline task to run integration tests that need KeyVault secrets successfully, without exposing any secrets in source control:使用Azure CLI 管道任务成功运行需要 KeyVault 机密的集成测试,而不会在源代码控制中公开任何机密:

  1. Create a Service Principal service connection in your Azure DevOps project.在 Azure DevOps 项目中创建服务主体服务连接

  2. Give the principal Get and List permissions to the Vault in Azure.授予主体获取列出Azure 中 Vault 的权限。

  3. Run your integration tests inside an Azure CLI task :在 Azure CLI 任务中运行集成测试:

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

    This works because the tests will run in the context of azure cli, which is where AzureServiceTokenProvider tries fetching a token from before it fails .这是有效的,因为测试将在 azure cli 的上下文中运行,这是AzureServiceTokenProvider 在失败之前尝试AzureServiceTokenProvider 获取令牌的地方 Azure CLI handles the authentication and cleans up when the task is done. Azure CLI 处理身份验证并在任务完成后进行清理。

You should not do the integration test of authentication to Azure KeyVault within Azure DevOps Pipelines build, because you are using Azure DevOps default hosted agents.不应在 Azure DevOps Pipelines 构建中对 Azure KeyVault 进行身份验证的集成测试,因为你使用的是 Azure DevOps 默认托管代理。

By default, the Azure DevOps Pipelines are using basic default hosted agents, and these hosted agents are not accessible from your Azure subscription.默认情况下,Azure DevOps Pipelines 使用基本的默认托管代理,并且无法从 Azure 订阅访问这些托管代理。 These are not surprising, because these hosted agents are common agents for all common build needs, including build/compile, running unit tests, getting test coverages, and all of these tasks has no other additional features such as having ActiveDirectory, database, and other actual authentication/requests to other party such as authentication to any Azure Keyvault.这些并不奇怪,因为这些托管代理是所有常见构建需求的通用代理,包括构建/编译、运行单元测试、获取测试覆盖率,并且所有这些任务都没有其他附加功能,例如拥有 ActiveDirectory、数据库和其他对其他方的实际身份验证/请求,例如对任何 Azure Keyvault 的身份验证。 Therefore these agents by default are not registered in your Azure subscription.因此,默认情况下这些代理未在您的 Azure 订阅中注册。

If you want to have successful integration tests for these special needs, you have to create your own agents for Azure DevOps Pipelines build and release.如果你希望针对这些特殊需求进行成功的集成测试,则必须为 Azure DevOps Pipelines 构建和发布创建自己的代理。 Therefore, there is no other way to force Azure DevOps default agent to run your KeyVault authentication tests, other than creating your own agents and configure your Azure DevOps to use your own agents.因此,除了创建您自己的代理并配置您的 Azure DevOps 以使用您自己的代理之外,没有其他方法可以强制 Azure DevOps 默认代理运行您的 KeyVault 身份验证测试。

To create your own agents, consult this documentation from Microsoft:要创建您自己的代理,请参阅 Microsoft 提供的此文档:

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

UPDATE 29th October, 2018 : 2018 年 10 月 29 日更新

For more clarity, I also reply for your "Update 3" workaround.为了更清楚,我还回复了您的“更新 3”解决方法。 There is no guarantee that your workaround will work nicely when Microsoft updates the Azure DevOps' default hosted agent.当 Microsoft 更新 Azure DevOps 的默认托管代理时,无法保证您的解决方法会很好地工作。 Therefore I also need to add more point: it's not a good practice to have integration test that relies on other party beyond the realm of your Azure DevOps Pipelines build such as connecting to a database server or using external authentications (even on Azure KeyVault) within your CI, especially if you are using Microsoft's default hosted agents.因此,我还需要补充一点:集成测试依赖于 Azure DevOps Pipelines 构建领域之外的其他方,例如连接到数据库服务器或使用外部身份验证(即使在 Azure KeyVault 上),这不是一个好的做法。您的 CI,尤其是当您使用 Microsoft 的默认托管代理时。

Not just it will be error-prone due to invalid authentication configuration, but there's no guarantee that the further updates on the default hosted-agents would guarantee your third-party logic test will work.不仅由于无效的身份验证配置而容易出错,而且不能保证默认托管代理的进一步更新将保证您的第三方逻辑测试能够正常工作。

Running into the exact same issue myself.我自己遇到了完全相同的问题。 I did get a little further by modifying the code by adding a connection string to the AzureServiceTokenProvider (The default parameter passed is null).通过向 AzureServiceTokenProvider 添加连接字符串(传递的默认参数为空)修改代码,我确实得到了进一步的提升。 I still didn't get it to fully work though, maybe since the Azure DevOps user may or may not have the required access to the KeyVault, but I did not get an opportunity to dig in further.我仍然没有让它完全工作,也许是因为 Azure DevOps 用户可能有也可能没有对 KeyVault 的访问权限,但我没有机会进一步挖掘。 Hoping there is a better solution posted here.希望这里有更好的解决方案。

Update We added the Build user into the Azure AD and then added it to the Access Policies within the KeyVault to the user.更新我们将 Build 用户添加到 Azure AD 中,然后将其添加到 KeyVault 中的用户访问策略中。 Granting it only Get Access (Our test was only testing whether it could gather the secret).只授予它获取访问权限(我们的测试只是测试它是否可以收集秘密)。 Tests pass successfully now.现在测试成功通过。

An easier solution would be to use Azure DevOps Variable Groups .一个更简单的解决方案是使用 Azure DevOps 变量组

Someone with read permissions and contributor on the DevOps project can create the variable group, link it to the key vault and select the desired secrets.拥有 DevOps 项目读取权限和贡献者的人可以创建变量组,将其链接到密钥保管库并选择所需的机密。

The variable group can now be linked to any of your pipelines.变量组现在可以链接到您的任何管道。

However to make it available to any code running in the pipeline you must first export the secrets using this method.但是,要使其可用于管道中运行的任何代码,您必须首先使用此方法导出机密。

You need to do this via a task (Azure Powershell or Bash) but it must be done via an inline script.您需要通过任务(Azure Powershell 或 Bash)完成此操作,但必须通过内联脚本完成。 You cannot export the keyvault variables in a script in a file in the t ask.您不能在 t ask 文件中的脚本中导出密钥库变量。 So in the first task export all your variables and all the subsequent tasks and referenced scripts can consume them.因此,在第一个任务中导出所有变量,所有后续任务和引用的脚本都可以使用它们。

PowerShell:电源外壳:

Write-Host "##vso[task.setvariable variable=mysecretexported]$(mysecret1)"
Bash
@echo ##vso[task.setvariable variable=mysecretexported]$(mysecret1)"

You can then refer to the secret using the exported variable

Powershell

Write-Host No problem reading "$env:MYSECRETEXPORTED"

Batch:批:

@echo No problem reading %mysecretexported%

Bash works similar: Bash 的工作原理类似:

#!/bin/bash

echo "No problem reading $MYSECRETEXPORTED"

This is also supported in YAML这在 YAML 中也受支持

The nice part is that these variables will be masked in your logs so your secrets stay secret.好的部分是这些变量将在您的日志中被屏蔽,因此您的秘密将保密。

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

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