草庐IT

c# - VSTS生成管道:测试无法连接到Azure Key Vault

coder 2024-05-25 原文

我正在尝试使用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>();

运行构建管道时,我正在使用Hosted VS2017实例构建项目。除了试图访问密钥库的集成测试失败之外,其他所有程序都在运行。我正在使用以下软件包:
  • Microsoft.Azure.Services.AppAuthentication -易于获取
    服务到Azure服务身份验证方案的访问 token 。
  • 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)
    

    更新资料

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

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

    更新2

    我仍然没有找到解决方案。我迷失了如何使我的管道顺利运行测试的问题。我看到发布管道也可以选择运行测试。但是这些似乎需要.dll文件,而我的构建管道放置文件只有Web应用程序(我看不到任何测试项目发布的放置文件)。不知道这是否有可能。

    更新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中创建的具有对 secret 的读取权限的应用程序。
  • 修改了我对 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}")
    

  • 请注意,您的客户 secret 必须正确设置格式。应用程序注册(预览)会生成一个随 secret 钥。有时,此键在连接字符串中不起作用(格式错误会引发错误)。尝试在非预览版的应用程序注册中生成您自己的密钥,或者生成一个新密钥,然后重试。

    之后,我能够在构建管道中成功运行集成测试,并在Azure中创建到Web应用程序的发行版。我对这种方法不满意,因为尽管它可以工作,但是它在代码本身中暴露了一个 secret 值。由于上述方法,不需要打开管理服务身份。在这方面,我感到这非常糟糕。

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

    最佳答案

    您不应在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/en-us/azure/devops/pipelines/agents/agents?view=vsts#install

    更新于2018年10月29日:

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

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

    关于c# - VSTS生成管道:测试无法连接到Azure Key Vault,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52827004/

    有关c# - VSTS生成管道:测试无法连接到Azure Key Vault的更多相关文章

    1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

      很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

    3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    4. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

      我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

    5. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

      我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

    6. ruby - 无法运行 Rails 2.x 应用程序 - 2

      我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

    7. ruby - Ruby 的 Hash 在比较键时使用哪种相等性测试? - 2

      我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。

    8. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

      在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

    9. ruby - RSpec - 使用测试替身作为 block 参数 - 2

      我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

    10. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

      我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

    随机推荐