Я пытаюсь использовать VSTS (теперь Azure DevOps) для создания конвейера CI / CD. Для моего конвейера сборки у меня есть очень простая настройка, включающая выполнение шагов восстановления, сборки, тестирования и публикации.
На моем этапе тестирования я настроил его для запуска двух тестовых проектов - одного проекта модульного тестирования и одного проекта тестирования интеграции. Моя политика доступа к Key Vault настроена для предоставления доступа как мне, так и 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>();
Когда я запускаю конвейер сборки, я использую размещенный экземпляр VS2017 для сборки моего проекта. Все работает, за исключением интеграционных тестов, которые не могут получить доступ к хранилищу ключей. Я использую следующие пакеты:
- Microsoft.Azure.Services.AppAuthentication - упрощает получение токенов доступа для сценариев аутентификации Service-to-Azure-Service.
- Microsoft.Azure.KeyVault - содержит методы для взаимодействия с Key Vault.
- Microsoft.Extensions.Configuration.AzureKeyVault - содержит
расширения IConfiguration для Azure Key Vault.
Я следил за этим руководством 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)
Обновлять
Я нашел только одно сообщение, связанное с этой проблемой: https://social.msdn.microsoft.com/Forums/en-US/0bac778a-283a-4be1-bc75-605e776adac0/managed-service-identity-проблема?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 создайте новый секрет клиента
Предоставьте новому приложению AD доступ к хранилищу ключей. Зайдите в политики доступа к хранилищу ключей и добавьте созданное вами в AD приложение с доступом для чтения к вашим секретам.
Изменил мой вызов 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. Я не удовлетворен этим подходом, потому что, хотя он работает, он раскрывает секретное значение в самом коде. Управляет удостоверением службы не нужно включать из-за вышеупомянутого подхода. Я считаю, что в этом отношении это очень плохо.
Должен быть способ получше. Один из вариантов - не запускать интеграционный тест в конвейере сборки. Не уверен, что это правильный подход. Я все еще надеюсь, что кто-то сможет предложить лучший подход к этому или объяснить, подходит ли мой подход для использования.