VSTS Build Pipeline: тест не подходит для подключения к хранилищу ключей Azure Key

Я пытаюсь использовать VSTS (теперь Azure DevOps) для создания конвейера CI/CD. Для моего конвейера сборки у меня есть очень простая настройка, включающая выполнение шагов восстановления, сборки, тестирования и публикации.

Для моего тестового шага у меня есть настройка для запуска двух тестовых проектов - одного модульного тестового проекта и одного интеграционного тестового проекта. У меня есть настройка политики доступа к ключам Key для обеспечения доступа как к себе, так и к 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 - позволяет легко извлекать токены доступа для сценариев аутентификации Service-to-Azure-Service.
  • Microsoft.Azure.KeyVault - содержит методы взаимодействия с Key Vault.
  • Microsoft.Extensions.Configuration.AzureKeyVault - содержит
    Расширения IConfiguration для хранилища ключей Azure

Я выполнил этот учебник https://docs.microsoft.com/en-us/azure/key-vault/tutorial-web-application-keyvault, чтобы настроить хранилище ключей и интегрировать его в мое приложение.

Я просто пытаюсь заставить свою сборку работать, убедившись, что пройдут тестирование модуля и интеграции. Я пока не развертываю его в службе приложений. Модульные тесты запускаются без каких-либо проблем, поскольку я издеваюсь над различными службами. Мой тест интеграции не работает с сообщениями об ошибках ниже. Как получить доступ к тестовому доступу к хранилищу ключей? Нужно ли добавлять какие-либо специальные политики доступа в мое хранилище ключей для размещенной сборки VS2017? Не уверен, что делать, поскольку я не вижу ничего, что выделяется.

Build

Ниже приведена трассировка стека для ошибки:

    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)

Обновить

Я нашел только одну связанную запись по этой проблеме: https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-issue?forum=windowsazurewebsitespreview, Но сообщение связано с развертыванием приложения в лазурном слоте. Я просто пытаюсь создать свое приложение в конвейере сборки.

Я все еще пытаюсь решить эту проблему, и я не уверен, что лучший способ обеспечить необходимый доступ.


Обновление 2

Я до сих пор не нашел для этого решения. Я теряюсь на том, как получить мой конвейер для запуска моего теста без проблем. Я видел, что в конвейере выпуска есть варианты запуска тестов. Но, похоже, они берут файлы.dll, а файл с откатным конвейером сборки имеет только веб-приложение (я не вижу ни одного из опубликованных файлов пробных проектов). Не уверен, что это даже возможность.


Обновление 3

Мне удалось заставить его работать, используя последний предоставленный здесь вариант: https://docs.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#connection-string-support

Я попробовал другие способы использования сертификата, но в любое время {CurrentUser} предоставляется в строке подключения, сбой конвейера сборки. Он работает на моей локальной машине, но не в конвейере сборки.

Чтобы заставить его работать, мне пришлось сделать три вещи:

  • Войдите в Azure. Настройка регистрации нового приложения в Azure AD
  • В вашей новой регистрации приложения AD создайте новый клиентский секрет enter image description here
  • Предоставьте свой новый доступ к приложениям AD в хранилище ключей. Перейдите в свои политики доступа к хранилищу ключей и добавьте приложение, созданное в вашем AD, с доступом для чтения к вашим секретам. enter image description here

  • Изменен мой вызов AzureServiceTokenProvier() в файле Program.cs следующим образом:

     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}")
    

Обратите внимание, что секрет вашего клиента должен быть отформатирован правильно. Регистрация приложений (предварительный просмотр) генерирует случайный секретный ключ. Иногда этот ключ не работает в строке соединения (выдает ошибку как неправильно отформатированную). Либо попробуйте создать свой собственный ключ в версии предварительного просмотра без предварительного просмотра, либо сгенерируйте новый ключ и повторите попытку.

После этого я смог успешно запустить мой интеграционный тест в моем конвейере сборки и создать выпуск для моего веб-приложения в Azure. Я не удовлетворен этим подходом, потому что, хотя он работает, он раскрывает секретное значение в самом коде. Управление идентификацией службы не обязательно должно быть включено из-за вышеуказанного подхода. Я чувствую, что это очень плохо в этом отношении.

Должен быть лучший путь, чем это. Один из вариантов - не запускать интеграционный тест в конвейере сборки. Не уверен, что это правильный подход. Я все еще надеюсь, что кто-то сможет обеспечить лучший подход к этому или объяснить, подходит ли мой подход.

Ответ 1

Не следует выполнять интеграционный тест аутентификации в Azure KeyVault в составе Azure DevOps Pipelines, потому что вы используете агенты, поддерживающие по умолчанию Azure DevOps.

По умолчанию в Azure DevOps Pipelines используются базовые обработчики по умолчанию, и эти размещенные агенты недоступны из вашей подписки Azure. Это неудивительно, поскольку эти размещенные агенты являются обычными агентами для всех общих потребностей сборки, включая сборку/компиляцию, выполнение модульных тестов, получение тестовых покрытий и все эти задачи не имеют других дополнительных функций, таких как наличие ActiveDirectory, базы данных и других фактическая аутентификация/запрос другой стороне, такой как аутентификация, любому Azure Keyvault. Поэтому эти агенты по умолчанию не зарегистрированы в вашей подписке Azure.

Если вы хотите иметь успешные интеграционные тесты для этих особых потребностей, вам необходимо создать свои собственные агенты для сборки и выпуска Azure DevOps Pipelines. Поэтому нет другого способа заставить агента по умолчанию Azure DevOps запускать тесты проверки подлинности KeyVault, кроме создания собственных агентов и настройки Azure DevOps для использования ваших собственных агентов.

Чтобы создать свои собственные агенты, обратитесь к этой документации от Microsoft:

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

ОБНОВЛЕНИЕ 29 октября 2018 года:

Для большей ясности я также отвечаю за обходное решение "Обновление 3". Нет никакой гарантии, что ваш метод работы будет хорошо работать, когда Microsoft обновит агент, поддерживающий по умолчанию Azure DevOps. Поэтому мне также нужно добавить еще одно замечание: неплохо провести интеграционный тест, который опирается на другую сторону, выходящую за рамки построения ваших Azure DevOps Pipelines, таких как подключение к серверу базы данных или использование внешних аутентификаций (даже на Azure KeyVault) в пределах ваш CI, особенно если вы используете агенты, размещенные по умолчанию Microsoft.

Не только он будет подвержен ошибкам из-за недействительной конфигурации аутентификации, но не гарантирует, что дальнейшие обновления для хост-агентов по умолчанию гарантируют, что ваш сторонний логический тест будет работать.

Ответ 2

Идти в ту же самую проблему сам. Я немного поправился, изменив код, добавив строку соединения в AzureServiceTokenProvider (переданный по умолчанию параметр равен null). Я все еще не получил его, чтобы полностью работать, хотя, возможно, так как пользователь Azure DevOps может иметь или не иметь необходимый доступ к KeyVault, но у меня не было возможности копать дальше. Надеемся, что здесь будет лучшее решение.

Обновление Мы добавили пользователя Build в Azure AD, а затем добавили его в Политики доступа в KeyVault для пользователя. Предоставление только доступа (наш тест проверял только, может ли он собрать секрет). Тесты проходят успешно.

Ответ 3

Используйте задачу конвейера Azure CLI для успешного запуска интеграционных тестов, для которых необходимы секреты KeyVault, без раскрытия каких-либо секретов в управлении исходным кодом:

  1. Создайте подключение службы субъекта-службы в своем проекте DevOps Azure.

  2. Предоставьте основным разрешениям Get и List хранилище в Azure.

  3. Запустите интеграционные тесты внутри задачи CLI Azure:

    - task: [email protected]
      inputs:
        azureSubscription: 'Your Service Connection Name'
        scriptLocation: 'inlineScript'
        inlineScript: 'dotnet test --configuration $(buildConfiguration) --logger trx'
    

    Это работает, потому что тесты будут выполняться в контексте AzureServiceTokenProvider где AzureServiceTokenProvider пытается AzureServiceTokenProvider токен до того, как он завершится неудачей. Azure CLI обрабатывает аутентификацию и очищает, когда задача выполнена.