Поставщик конфигурации Azure Key Vault в ASP.NET Core

В этой статье объясняется, как использовать поставщик конфигурации Azure Key Vault для загрузки значений конфигурации приложения из секретов Azure Key Vault. Azure Key Vault — это облачная служба, которая помогает защитить криптографические ключи и секреты, используемые приложениями и службами. Распространенные сценарии использования Azure Key Vault с приложениями ASP.NET Core включают:

  • Управление доступом к конфиденциальным данным конфигурации.
  • Выполнение требования для FIPS 140-2 уровня 2, проверенных аппаратными модулями безопасности (HSM) при хранении данных конфигурации.

Пакеты

Добавьте ссылки на пакеты для следующих пакетов:

Пример приложения

Пример приложения выполняется в любом из двух режимов, #define определенных директивой препроцессора в верхней части Program.cs:

  • Certificate: демонстрирует использование идентификатора клиента Azure Key Vault и сертификата X.509 для доступа к секретам, хранящимся в Azure Key Vault. Этот пример можно запустить из любого расположения, будь то развернутые в службе приложение Azure или любом узле, который может служить приложением ASP.NET Core.
  • Managed: демонстрирует использование управляемых удостоверений для ресурсов Azure. Управляемое удостоверение проверяет подлинность приложения в Azure Key Vault с помощью проверки подлинности Azure Active Directory (AD) без хранения учетных данных в коде или конфигурации приложения. Версия Managed примера должна быть развернута в Azure. Следуйте инструкциям в разделе "Использование управляемых удостоверений для ресурсов Azure".

Дополнительные сведения о настройке примера приложения с помощью директив препроцессора (#define) см. в обзоре ASP.NET Core.

Просмотреть или скачать образец кода (описание загрузки)

Хранилище секретов в среде разработки

Задайте секреты локально с помощью диспетчера секретов. При запуске примера приложения на локальном компьютере в среде разработки секреты загружаются из локального хранилища секретов пользователей.

Для диспетчера секретов требуется <UserSecretsId> свойство в файле проекта приложения. Задайте значение свойства ({GUID}) для любого уникального GUID:

<PropertyGroup>
  <UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>

Секреты создаются в виде пар "имя-значение". Иерархические значения (разделы конфигурации) используют ( : двоеточие) в качестве разделителя в именах ключей конфигурации ASP.NET Core.

Диспетчер секретов используется из командной оболочки, открытой в корневом каталоге содержимого проекта, где {SECRET NAME} находится имя и {SECRET VALUE} является значением:

dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"

Выполните следующие команды в командной оболочке из корневого каталога содержимого проекта, чтобы задать секреты для примера приложения:

dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"

Если эти секреты хранятся в Azure Key Vault в хранилище секретов в рабочей среде с разделом Azure Key Vault , _dev суффикс изменяется на _prod. Суффикс предоставляет визуальный сигнал в выходных данных приложения, указывающий источник значений конфигурации.

Хранилище секретов в рабочей среде с помощью Azure Key Vault

Выполните следующие действия, чтобы создать Azure Key Vault и сохранить в нем секреты примера приложения. Дополнительные сведения см . в кратком руководстве по настройке и извлечению секрета из Azure Key Vault с помощью Azure CLI.

  1. Откройте Azure Cloud Shell с помощью одного из следующих методов в портал Azure:

    • Нажмите кнопку Попробовать в правом верхнем углу блока с кодом. Используйте строку поиска Azure CLI в текстовом поле.
    • Откройте Cloud Shell в браузере с помощью кнопки Launch Cloud Shell .
    • Нажмите кнопку меню Cloud Shell в правом верхнем углу окна портала Azure.

    Дополнительные сведения см. в Azure CLI и обзоре Azure Cloud Shell.

  2. Если вы еще не прошли проверку подлинности, выполните вход с помощью az login команды.

  3. Создайте группу ресурсов со следующей командой, где {RESOURCE GROUP NAME} имя новой группы ресурсов и {LOCATION} регион Azure:

    az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  4. Создайте Key Vault в группе ресурсов с помощью следующей команды, где {KEY VAULT NAME} находится имя нового хранилища и {LOCATION} является регионом Azure:

    az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  5. Создайте секреты в хранилище в виде пар "имя-значение".

    Имена секретов Azure Key Vault ограничены буквенно-цифровыми символами и дефисами. Иерархические значения (разделы конфигурации) используют -- (два дефиса) в качестве разделителя, так как двоеточия не допускаются в именах секретов Key Vault. Двоеточие разделит раздел из подраздела в конфигурации ASP.NET Core. Двухтирная последовательность заменяется двоеточием при загрузке секретов в конфигурацию приложения.

    Следующие секреты предназначены для использования с примером приложения. Значения включают _prod суффикс, чтобы отличить их от _dev значений суффикса, загруженных в среде разработки из Secret Manager. Замените {KEY VAULT NAME} именем хранилища ключей, созданного на предыдущем шаге:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
    

Использование идентификатора приложения и сертификата X.509 для приложений, не размещенных в Azure

Настройте Azure AD, Azure Key Vault и приложение для использования идентификатора приложения Azure AD и сертификата X.509 для проверки подлинности в хранилище при размещении приложения за пределами Azure. См. дополнительные сведения о ключах, секретах и сертификатах.

Примечание.

Хотя использование идентификатора приложения и сертификата X.509 поддерживается для приложений, размещенных в Azure, не рекомендуется. Вместо этого используйте управляемые удостоверения для ресурсов Azure при размещении приложения в Azure. Управляемые удостоверения не требуют хранения сертификата в приложении или в среде разработки.

В примере приложения используется идентификатор приложения и сертификат X.509, когда #define директива препроцессора в верхней части Program.cs задана Certificate.

  1. Создайте сертификат архива PKCS#12 (PFX). Параметры создания сертификатов включают New-SelfSignedCertificate в Windows и OpenSSL.
  2. Установите сертификат в личное хранилище сертификатов текущего пользователя. Маркировка ключа как экспортируемого является необязательным. Обратите внимание на отпечаток сертификата, который используется позже в этом процессе.
  3. Экспортируйте сертификат архива PKCS#12 (PFX) в виде сертификата в кодировке DER (CER).
  4. Зарегистрируйте приложение в Azure AD (Регистрация приложений).
  5. Отправьте сертификат в кодировке DER (CER) в Azure AD:
    1. Выберите приложение в Azure AD.
    2. Перейдите к сертификатам и секретам.
    3. Выберите " Отправить сертификат", чтобы отправить сертификат , содержащий открытый ключ. Сертификат CER, PEM или CRT является допустимым.
  6. Сохраните имя Key Vault, идентификатор приложения и отпечаток сертификата в файле приложения appsettings.json .
  7. Перейдите в Key Vault в портал Azure.
  8. Выберите хранилище ключей, созданное в хранилище секретов в рабочей среде, в разделе Azure Key Vault .
  9. Выберите Политики доступа.
  10. Выберите Добавить политику доступа.
  11. Откройте разрешения секрета и предоставьте приложению разрешения get and List .
  12. Выберите субъект и выберите зарегистрированного приложения по имени. Выберите кнопку Выбрать.
  13. Нажмите ОК.
  14. Выберите Сохранить.
  15. Разверните приложение.

Пример Certificate приложения получает значения конфигурации из IConfigurationRoot того же имени, что и имя секрета:

  • Не иерархические значения: значение для SecretName получения config["SecretName"]с.
  • Иерархические значения (разделы): используйте : нотацию (двоеточие GetSection ) или метод. Используйте любой из этих подходов для получения значения конфигурации:
    • config["Section:SecretName"]
    • config.GetSection("Section")["SecretName"]

Сертификат X.509 управляется ОС. Вызовы AddAzureKeyVault приложения со значениями, предоставленными файлом appsettings.json :


using System.Security.Cryptography.X509Certificates;
using Azure.Identity;

var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsProduction())
{
    using var x509Store = new X509Store(StoreLocation.CurrentUser);

    x509Store.Open(OpenFlags.ReadOnly);

    var x509Certificate = x509Store.Certificates
        .Find(
            X509FindType.FindByThumbprint,
            builder.Configuration["AzureADCertThumbprint"],
            validOnly: false)
        .OfType<X509Certificate2>()
        .Single();

    builder.Configuration.AddAzureKeyVault(
        new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
        new ClientCertificateCredential(
            builder.Configuration["AzureADDirectoryId"],
            builder.Configuration["AzureADApplicationId"],
            x509Certificate));
}

var app = builder.Build();

Примеры значений:

  • Имя Key Vault: contosovault
  • ИД приложения: 627e911e-43cc-61d4-992e-12db9c81b413
  • Отпечаток сертификата: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:

{
  "KeyVaultName": "Key Vault Name",
  "AzureADApplicationId": "Azure AD Application ID",
  "AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
  "AzureADDirectoryId": "Azure AD Directory ID"
}

При запуске приложения веб-страница отображает загруженные значения секретов. В среде разработки значения секретов загружают суффиксом _dev . В рабочей среде значения загружают суффикс _prod .

Использование управляемых удостоверений для ресурсов Azure

Приложение, развернутое в Azure , может воспользоваться управляемыми удостоверениями для ресурсов Azure. Управляемое удостоверение позволяет приложению проходить проверку подлинности в Azure Key Vault с помощью проверки подлинности Azure AD без хранения учетных данных в коде или конфигурации приложения.

В примере приложения используется управляемое удостоверение, назначаемое системой, если #define директива препроцессора в верхней части Program.cs задана Managed. Сведения о создании управляемого удостоверения для приложения службы приложение Azure см. в статье "Использование управляемых удостоверений для Служба приложений и Функции Azure". После создания управляемого удостоверения обратите внимание на идентификатор объекта приложения, показанный в портал Azure на Identity панели Служба приложений.

Введите имя хранилища в файл приложения appsettings.json . Пример приложения не требует идентификатора приложения и пароля (секрет клиента) при настройке Managed версии, поэтому эти записи конфигурации можно игнорировать. Приложение развертывается в Azure, и Azure проверяет подлинность приложения для доступа к Azure Key Vault только с помощью имени хранилища, хранящегося в appsettings.json файле.

Разверните пример приложения в службе приложение Azure.

С помощью Azure CLI и идентификатора объекта приложения предоставьте приложению list и get разрешения для доступа к хранилищу:

az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list

Перезапустите приложение с помощью Azure CLI, PowerShell или портал Azure.

Пример приложения создает экземпляр DefaultAzureCredential класса. Учетные данные пытаются получить маркер доступа из среды для ресурсов Azure:

using Azure.Identity;

var builder = WebApplication.CreateBuilder(args);

if (builder.Environment.IsProduction())
{
    builder.Configuration.AddAzureKeyVault(
        new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
        new DefaultAzureCredential());
}

Пример значения имени Key Vault: contosovault

appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

Для приложений, использующих управляемое удостоверение, назначаемое пользователем, настройте идентификатор клиента управляемого удостоверения с помощью одного из следующих подходов:

  1. Установите переменную среды AZURE_CLIENT_ID.

  2. Задайте свойство при вызове DefaultAzureCredentialOptions.ManagedIdentityClientIdAddAzureKeyVault:

    builder.Configuration.AddAzureKeyVault(
        new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
        new DefaultAzureCredential(new DefaultAzureCredentialOptions
        {
            ManagedIdentityClientId = builder.Configuration["AzureADManagedIdentityClientId"]
        }));
    

При запуске приложения веб-страница отображает загруженные значения секретов. В среде разработки значения секретов имеют _dev суффикс, так как они предоставляются диспетчером секретов. В рабочей среде значения загружаются суффиксом _prod , так как они предоставляются Azure Key Vault.

Если вы получите сообщение об ошибке Access denied , убедитесь, что приложение зарегистрировано в Azure AD и предоставляется доступ к хранилищу. Убедитесь, что вы перезагрузили службу в Azure.

Сведения об использовании поставщика с управляемым удостоверением и Azure Pipelines см. в статье "Создание подключения службы Azure Resource Manager к виртуальной машине с управляемым удостоверением службы".

Варианты конфигурации

AddAzureKeyVault может принять AzureKeyVaultConfigurationOptions объект:

// using Azure.Extensions.AspNetCore.Configuration.Secrets;

builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
    new DefaultAzureCredential(),
    new AzureKeyVaultConfigurationOptions
    {
        // ...
    });

Объект AzureKeyVaultConfigurationOptions содержит следующие свойства:

Свойство Description
Manager KeyVaultSecretManager экземпляр, используемый для управления загрузкой секретов.
ReloadInterval TimeSpan ожидание между попытками опроса хранилища для внесения изменений. Значением по умолчанию является null (конфигурация не перезагрузится).

Использование префикса имени ключа

AddAzureKeyVault предоставляет перегрузку, которая принимает реализацию KeyVaultSecretManager, которая позволяет управлять преобразованием секретов Key Vault в ключи конфигурации. Например, интерфейс можно реализовать для загрузки значений секретов на основе значения префикса, предоставленного при запуске приложения. Этот метод позволяет, например, загружать секреты на основе версии приложения.

Предупреждение

Не используйте префиксы в секретах Key Vault для:

  • Разместите секреты для нескольких приложений в одном хранилище.
  • Поместите секреты окружающей среды (например, разработку и производственные секреты) в то же хранилище.

Различные приложения и среды разработки и рабочей среды должны использовать отдельные хранилища ключей для изоляции сред приложений для наивысшего уровня безопасности.

В следующем примере секрет устанавливается в Key Vault (и с помощью диспетчера секретов для среды разработки) в течение 5000-AppSecret (периоды не допускаются в именах секретов Key Vault). Этот секрет представляет секрет приложения для версии 5.0.0.0.0 приложения. Для другой версии приложения 5.1.0.0 секрет добавляется в хранилище (и с помощью диспетчера секретов).5100-AppSecret Каждая версия приложения загружает значение секрета версии в конфигурацию, как AppSecretудаление версии при загрузке секрета.

AddAzureKeyVault вызывается с пользовательской KeyVaultSecretManager реализацией:

// using Azure.Extensions.AspNetCore.Configuration.Secrets;

builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
    new DefaultAzureCredential(),
    new SamplePrefixKeyVaultSecretManager("5000"));

Реализация реагирует на префиксы версий секретов, чтобы загрузить соответствующий секрет в конфигурацию:

  • Load загружает секрет, когда его имя начинается с префикса. Другие секреты не загружаются.
  • GetKey:
    • Удаляет префикс из имени секрета.
    • Заменяет два дефиса в любом имени KeyDelimiterна разделитель, используемый в конфигурации (обычно двоеточие). Azure Key Vault не разрешает двоеточие в именах секретов.
public class SamplePrefixKeyVaultSecretManager : KeyVaultSecretManager
{
    private readonly string _prefix;

    public SamplePrefixKeyVaultSecretManager(string prefix)
        => _prefix = $"{prefix}-";

    public override bool Load(SecretProperties properties)
        => properties.Name.StartsWith(_prefix);

    public override string GetKey(KeyVaultSecret secret)
        => secret.Name[_prefix.Length..].Replace("--", ConfigurationPath.KeyDelimiter);
}

Метод Load вызывается алгоритмом поставщика, который выполняет итерацию по секретам хранилища, чтобы найти секреты, префиксированные версией. При обнаружении Loadпрефикса версии алгоритм использует GetKey метод для возврата имени конфигурации имени секрета. Он удаляет префикс версии из имени секрета. Остальная часть имени секрета возвращается для загрузки в пары "Имя-значение" конфигурации приложения.

При реализации этого подхода:

  1. Версия приложения, указанная в файле проекта приложения. В следующем примере для версии приложения задано 5.0.0.0значение :

    <PropertyGroup>
      <Version>5.0.0.0</Version>
    </PropertyGroup>
    
  2. Убедитесь, что <UserSecretsId> свойство присутствует в файле проекта приложения, где {GUID} указан guid, предоставленный пользователем:

    <PropertyGroup>
      <UserSecretsId>{GUID}</UserSecretsId>
    </PropertyGroup>
    

    Сохраните следующие секреты локально с помощью диспетчера секретов:

    dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev"
    dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
    
  3. Секреты сохраняются в Azure Key Vault с помощью следующих команд Azure CLI:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
    
  4. При запуске приложения загружаются секреты Key Vault. Строковый секрет 5000-AppSecret соответствует версии приложения, указанной в файле проекта приложения (5.0.0.0).

  5. Версия ( 5000 с дефисом) удаляется из имени ключа. В приложении конфигурация чтения с ключом AppSecret загружает значение секрета.

  6. Если версия приложения изменяется в файле 5.1.0.0 проекта и выполняется снова, возвращается 5.1.0.0_secret_value_dev значение секрета в среде разработки и 5.1.0.0_secret_value_prod в рабочей среде.

Примечание.

Вы также можете предоставить собственную SecretClient реализацию AddAzureKeyVault. Настраиваемый клиент разрешает общий доступ к одному экземпляру клиента в приложении.

Привязка массива к классу

Поставщик может считывать значения конфигурации в массив для привязки к массиву POCO.

При чтении из источника конфигурации, позволяющего ключам содержать разделители двоеточий (:) числовой сегмент ключа используется для различения ключей, составляющих массив (:0:, :1:... :{n}:). Дополнительные сведения см. в разделе "Конфигурация: привязка массива к классу".

Ключи Azure Key Vault не могут использовать двоеточие в качестве разделителя. Описанный в этой статье подход использует двойные дефисы (--) в качестве разделителя для иерархических значений (разделов). Ключи массива хранятся в Azure Key Vault с двойными дефисами и числовыми сегментами ключей (--0--, --1--... --{n}--).

Изучите следующую конфигурацию поставщика ведения журнала Serilog , предоставляемую ON-файлом JS. В массиве определены WriteTo два литерала объектов, которые отражают два приемника Serilog, описывающие назначения для выходных данных ведения журнала:

"Serilog": {
  "WriteTo": [
    {
      "Name": "AzureTableStorage",
      "Args": {
        "storageTableName": "logs",
        "connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
      }
    },
    {
      "Name": "AzureDocumentDB",
      "Args": {
        "endpointUrl": "https://contoso.documents.azure.com:443",
        "authorizationKey": "Eby8...GMGw=="
      }
    }
  ]
}

Конфигурация, показанная в предыдущем JSON-файле, хранится в Azure Key Vault с помощью двойного дефиса (--) нотации и числовых сегментов:

Ключ Значение
Serilog--WriteTo--0--Name AzureTableStorage
Serilog--WriteTo--0--Args--storageTableName logs
Serilog--WriteTo--0--Args--connectionString DefaultEnd...ountKey=Eby8...GMGw==
Serilog--WriteTo--1--Name AzureDocumentDB
Serilog--WriteTo--1--Args--endpointUrl https://contoso.documents.azure.com:443
Serilog--WriteTo--1--Args--authorizationKey Eby8...GMGw==

Перезагрузить секреты

По умолчанию секреты кэшируются поставщиком конфигурации для времени существования приложения. Секреты, которые впоследствии были отключены или обновлены в хранилище, игнорируются приложением.

Чтобы перезагрузить секреты, вызовите:IConfigurationRoot.Reload

config.Reload();

Чтобы периодически загружать секреты, задается свойство с заданным интервалом AzureKeyVaultConfigurationOptions.ReloadInterval . Дополнительные сведения см. в разделе Параметры конфигурации.

Отключенные и истекшие секреты

Секреты с истекшим сроком действия включаются по умолчанию в поставщик конфигурации. Чтобы исключить значения этих секретов в конфигурации приложения, обновите истекший срок действия или укажите конфигурацию с помощью пользовательского поставщика конфигурации:

class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
  public override bool Load(SecretProperties properties) =>
    properties.ExpiresOn.HasValue &&
    properties.ExpiresOn.Value > DateTimeOffset.Now;
}

Передайте этот пользовательский KeyVaultSecretManager код в AddAzureKeyVault:

// using Azure.Extensions.AspNetCore.Configuration.Secrets;

builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
    new DefaultAzureCredential(),
    new SampleKeyVaultSecretManager());

Отключенные секреты не могут быть извлечены из Key Vault и никогда не включены.

Устранение неполадок

Если приложению не удается загрузить конфигурацию с помощью поставщика, сообщение об ошибке записывается в инфраструктуру ASP.NET core Logging. Следующие условия препятствуют загрузке конфигурации:

  • Приложение или сертификат настроены неправильно в Azure AD.
  • Хранилище не существует в Azure Key Vault.
  • Приложение не авторизовано для доступа к хранилищу.
  • Политика доступа не включает Get и List разрешения.
  • В хранилище данные конфигурации (пара "имя-значение") неправильно называются, отсутствуют или отключены.
  • Приложение имеет неправильное имя Key Vault (KeyVaultName), идентификатор приложения Azure AD (AzureADApplicationId), или отпечаток сертификата Azure AD (AzureADCertThumbprint), или идентификатор azure AD Directory (AzureADDirectoryId).
  • При добавлении политики доступа Key Vault для приложения была создана политика, но кнопка "Сохранить " не была выбрана в пользовательском интерфейсе политик доступа.

Дополнительные ресурсы

В этой статье объясняется, как использовать поставщик конфигурации Azure Key Vault для загрузки значений конфигурации приложения из секретов Azure Key Vault. Azure Key Vault — это облачная служба, которая помогает защитить криптографические ключи и секреты, используемые приложениями и службами. Распространенные сценарии использования Azure Key Vault с приложениями ASP.NET Core включают:

  • Управление доступом к конфиденциальным данным конфигурации.
  • Выполнение требования для FIPS 140-2 уровня 2, проверенных аппаратными модулями безопасности (HSM) при хранении данных конфигурации.

Пакеты

Добавьте ссылки на пакеты для следующих пакетов:

Пример приложения

Пример приложения выполняется в любом из двух режимов, #define определенных директивой препроцессора в верхней части Program.cs:

  • Certificate: демонстрирует использование идентификатора клиента Azure Key Vault и сертификата X.509 для доступа к секретам, хранящимся в Azure Key Vault. Этот пример можно запустить из любого расположения, будь то развернутые в службе приложение Azure или любом узле, который может служить приложением ASP.NET Core.
  • Managed: демонстрирует использование управляемых удостоверений для ресурсов Azure. Управляемое удостоверение проверяет подлинность приложения в Azure Key Vault с помощью проверки подлинности Azure Active Directory (AD) без учетных данных, хранящихся в коде или конфигурации приложения. При использовании управляемых удостоверений для проверки подлинности идентификатор приложения Azure AD и пароль (секрет клиента) не требуется. Версия Managed примера должна быть развернута в Azure. Следуйте инструкциям в разделе "Использование управляемых удостоверений для ресурсов Azure".

Дополнительные сведения о настройке примера приложения с помощью директив препроцессора (#define) см. в обзоре ASP.NET Core.

Просмотреть или скачать образец кода (описание загрузки)

Хранилище секретов в среде разработки

Задайте секреты локально с помощью диспетчера секретов. При запуске примера приложения на локальном компьютере в среде разработки секреты загружаются из локального хранилища секретов пользователей.

Для диспетчера секретов требуется <UserSecretsId> свойство в файле проекта приложения. Задайте значение свойства ({GUID}) для любого уникального GUID:

<PropertyGroup>
  <UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>

Секреты создаются в виде пар "имя-значение". Иерархические значения (разделы конфигурации) используют ( : двоеточие) в качестве разделителя в именах ключей конфигурации ASP.NET Core.

Диспетчер секретов используется из командной оболочки, открытой в корневом каталоге содержимого проекта, где {SECRET NAME} находится имя и {SECRET VALUE} является значением:

dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"

Выполните следующие команды в командной оболочке из корневого каталога содержимого проекта, чтобы задать секреты для примера приложения:

dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"

Если эти секреты хранятся в Azure Key Vault в хранилище секретов в рабочей среде с разделом Azure Key Vault , _dev суффикс изменяется на _prod. Суффикс предоставляет визуальный сигнал в выходных данных приложения, указывающий источник значений конфигурации.

Хранилище секретов в рабочей среде с помощью Azure Key Vault

Выполните следующие действия, чтобы создать Azure Key Vault и сохранить в нем секреты примера приложения. Дополнительные сведения см . в кратком руководстве по настройке и извлечению секрета из Azure Key Vault с помощью Azure CLI.

  1. Откройте Azure Cloud Shell с помощью одного из следующих методов в портал Azure:

    • Нажмите кнопку Попробовать в правом верхнем углу блока с кодом. Используйте строку поиска Azure CLI в текстовом поле.
    • Откройте Cloud Shell в браузере с помощью кнопки Launch Cloud Shell .
    • Нажмите кнопку меню Cloud Shell в правом верхнем углу окна портала Azure.

    Дополнительные сведения см. в Azure CLI и обзоре Azure Cloud Shell.

  2. Если вы еще не прошли проверку подлинности, выполните вход с помощью az login команды.

  3. Создайте группу ресурсов со следующей командой, где {RESOURCE GROUP NAME} имя новой группы ресурсов и {LOCATION} регион Azure:

    az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  4. Создайте Key Vault в группе ресурсов с помощью следующей команды, где {KEY VAULT NAME} находится имя нового хранилища и {LOCATION} является регионом Azure:

    az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
    
  5. Создайте секреты в хранилище в виде пар "имя-значение".

    Имена секретов Azure Key Vault ограничены буквенно-цифровыми символами и дефисами. Иерархические значения (разделы конфигурации) используют -- (два дефиса) в качестве разделителя, так как двоеточия не допускаются в именах секретов Key Vault. Двоеточие разделит раздел из подраздела в конфигурации ASP.NET Core. Двухтирная последовательность заменяется двоеточием при загрузке секретов в конфигурацию приложения.

    Следующие секреты предназначены для использования с примером приложения. Значения включают _prod суффикс, чтобы отличить их от _dev значений суффикса, загруженных в среде разработки из Secret Manager. Замените {KEY VAULT NAME} именем хранилища ключей, созданного на предыдущем шаге:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
    

Использование идентификатора приложения и сертификата X.509 для приложений, не размещенных в Azure

Настройте Azure AD, Azure Key Vault и приложение для использования идентификатора приложения Azure AD и сертификата X.509 для проверки подлинности в хранилище при размещении приложения за пределами Azure. См. дополнительные сведения о ключах, секретах и сертификатах.

Примечание.

Хотя использование идентификатора приложения и сертификата X.509 поддерживается для приложений, размещенных в Azure, не рекомендуется. Вместо этого используйте управляемые удостоверения для ресурсов Azure при размещении приложения в Azure. Управляемые удостоверения не требуют хранения сертификата в приложении или в среде разработки.

В примере приложения используется идентификатор приложения и сертификат X.509, когда #define директива препроцессора в верхней части Program.cs задана Certificate.

  1. Создайте сертификат архива PKCS#12 (PFX). Параметры создания сертификатов включают New-SelfSignedCertificate в Windows и OpenSSL.
  2. Установите сертификат в личное хранилище сертификатов текущего пользователя. Маркировка ключа как экспортируемого является необязательным. Обратите внимание на отпечаток сертификата, который используется позже в этом процессе.
  3. Экспортируйте сертификат архива PKCS#12 (PFX) в виде сертификата в кодировке DER (CER).
  4. Зарегистрируйте приложение в Azure AD (Регистрация приложений).
  5. Отправьте сертификат в кодировке DER (CER) в Azure AD:
    1. Выберите приложение в Azure AD.
    2. Перейдите к сертификатам и секретам.
    3. Выберите " Отправить сертификат", чтобы отправить сертификат , содержащий открытый ключ. Сертификат CER, PEM или CRT является допустимым.
  6. Сохраните имя Key Vault, идентификатор приложения и отпечаток сертификата в файле приложения appsettings.json .
  7. Перейдите в Key Vault в портал Azure.
  8. Выберите хранилище ключей, созданное в хранилище секретов в рабочей среде, в разделе Azure Key Vault .
  9. Выберите Политики доступа.
  10. Выберите Добавить политику доступа.
  11. Откройте разрешения секрета и предоставьте приложению разрешения get and List .
  12. Выберите субъект и выберите зарегистрированного приложения по имени. Выберите кнопку Выбрать.
  13. Нажмите ОК.
  14. Выберите Сохранить.
  15. Разверните приложение.

Пример Certificate приложения получает значения конфигурации из IConfigurationRoot того же имени, что и имя секрета:

  • Не иерархические значения: значение для SecretName получения config["SecretName"]с.
  • Иерархические значения (разделы): используйте : нотацию (двоеточие GetSection ) или метод. Используйте любой из этих подходов для получения значения конфигурации:
    • config["Section:SecretName"]
    • config.GetSection("Section")["SecretName"]

Сертификат X.509 управляется ОС. Вызовы AddAzureKeyVault приложения со значениями, предоставленными файлом appsettings.json :

// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
// using Azure.Identity;

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();

                using var store = new X509Store(StoreLocation.CurrentUser);
                store.Open(OpenFlags.ReadOnly);
                var certs = store.Certificates.Find(
                    X509FindType.FindByThumbprint,
                    builtConfig["AzureADCertThumbprint"], false);

                config.AddAzureKeyVault(new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
                                        new ClientCertificateCredential(builtConfig["AzureADDirectoryId"], builtConfig["AzureADApplicationId"], certs.OfType<X509Certificate2>().Single()),
                                        new KeyVaultSecretManager());

                store.Close();
            }
        })
        .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());

Примеры значений:

  • Имя Key Vault: contosovault
  • ИД приложения: 627e911e-43cc-61d4-992e-12db9c81b413
  • Отпечаток сертификата: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:

{
  "KeyVaultName": "Key Vault Name",
  "AzureADApplicationId": "Azure AD Application ID",
  "AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
  "AzureADDirectoryId": "Azure AD Directory ID"
}

При запуске приложения веб-страница отображает загруженные значения секретов. В среде разработки значения секретов загружают суффиксом _dev . В рабочей среде значения загружают суффикс _prod .

Использование управляемых удостоверений для ресурсов Azure

Приложение, развернутое в Azure , может воспользоваться управляемыми удостоверениями для ресурсов Azure. Управляемое удостоверение позволяет приложению проходить проверку подлинности в Azure Key Vault с помощью проверки подлинности Azure AD без учетных данных (идентификатор приложения и секрет клиента или пароль), хранящихся в приложении.

В примере приложения используются управляемые удостоверения для ресурсов Azure, если #define в верхней части Program.cs задана Managedдиректива препроцессора.

Введите имя хранилища в файл приложения appsettings.json . Пример приложения не требует идентификатора приложения и пароля (секрет клиента) при настройке Managed версии, поэтому эти записи конфигурации можно игнорировать. Приложение развертывается в Azure, и Azure проверяет подлинность приложения для доступа к Azure Key Vault только с помощью имени хранилища, хранящегося в appsettings.json файле.

Разверните пример приложения в службе приложение Azure.

Приложение, развернутое в службе приложение Azure, автоматически регистрируется в Azure AD при создании службы. Получите идентификатор объекта из развертывания для использования в следующей команде. Идентификатор объекта отображается в портал Azure на Identity панели Служба приложений.

С помощью Azure CLI и идентификатора объекта приложения предоставьте приложению list и get разрешения для доступа к хранилищу:

az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list

Перезапустите приложение с помощью Azure CLI, PowerShell или портал Azure.

Пример приложения:

  • Создает экземпляр класса DefaultAzureCredential. Учетные данные пытаются получить маркер доступа из среды для ресурсов Azure.
  • Создается новый SecretClientDefaultAzureCredential экземпляр.
  • Экземпляр SecretClient используется с экземпляром KeyVaultSecretManager , который загружает значения секретов и заменяет двойные дефисы (--) двоеточием (:) в именах ключей.
// using Azure.Security.KeyVault.Secrets;
// using Azure.Identity;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            if (context.HostingEnvironment.IsProduction())
            {
                var builtConfig = config.Build();
                var secretClient = new SecretClient(
                    new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
                    new DefaultAzureCredential());
                config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());
            }
        })
        .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());

Пример значения имени Key Vault: contosovault

appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

При запуске приложения веб-страница отображает загруженные значения секретов. В среде разработки значения секретов имеют _dev суффикс, так как они предоставляются диспетчером секретов. В рабочей среде значения загружаются суффиксом _prod , так как они предоставляются Azure Key Vault.

Если вы получите сообщение об ошибке Access denied , убедитесь, что приложение зарегистрировано в Azure AD и предоставляется доступ к хранилищу. Убедитесь, что вы перезагрузили службу в Azure.

Сведения об использовании поставщика с управляемым удостоверением и Azure Pipelines см. в статье "Создание подключения службы Azure Resource Manager к виртуальной машине с управляемым удостоверением службы".

Варианты конфигурации

AddAzureKeyVault может принять AzureKeyVaultConfigurationOptions объект:

config.AddAzureKeyVault(
    new SecretClient(
        new Uri("Your Key Vault Endpoint"),
        new DefaultAzureCredential(),
        new AzureKeyVaultConfigurationOptions())
    {
        ...
    });

Объект AzureKeyVaultConfigurationOptions содержит следующие свойства.

Свойство Description
Manager KeyVaultSecretManager экземпляр, используемый для управления загрузкой секретов.
ReloadInterval TimeSpan ожидание между попытками опроса хранилища для внесения изменений. Значением по умолчанию является null (конфигурация не перезагрузится).

Использование префикса имени ключа

AddAzureKeyVault предоставляет перегрузку, которая принимает реализацию KeyVaultSecretManager, которая позволяет управлять преобразованием секретов Key Vault в ключи конфигурации. Например, интерфейс можно реализовать для загрузки значений секретов на основе значения префикса, предоставленного при запуске приложения. Этот метод позволяет, например, загружать секреты на основе версии приложения.

Предупреждение

Не используйте префиксы в секретах Key Vault для:

  • Разместите секреты для нескольких приложений в одном хранилище.
  • Поместите секреты окружающей среды (например, разработку и производственные секреты) в то же хранилище.

Различные приложения и среды разработки и рабочей среды должны использовать отдельные хранилища ключей для изоляции сред приложений для наивысшего уровня безопасности.

В следующем примере секрет устанавливается в Key Vault (и с помощью диспетчера секретов для среды разработки) в течение 5000-AppSecret (периоды не допускаются в именах секретов Key Vault). Этот секрет представляет секрет приложения для версии 5.0.0.0.0 приложения. Для другой версии приложения 5.1.0.0 секрет добавляется в хранилище (и с помощью диспетчера секретов).5100-AppSecret Каждая версия приложения загружает значение секрета версии в конфигурацию, как AppSecretудаление версии при загрузке секрета.

AddAzureKeyVault вызывается с пользовательской KeyVaultSecretManager реализацией:

config.AddAzureKeyVault(
    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
    builtConfig["AzureADApplicationId"],
    certs.OfType<X509Certificate2>().Single(),
    new PrefixKeyVaultSecretManager(versionPrefix));

Реализация реагирует на префиксы версий секретов, чтобы загрузить соответствующий секрет в конфигурацию:

  • Load загружает секрет, когда его имя начинается с префикса. Другие секреты не загружаются.
  • GetKey:
    • Удаляет префикс из имени секрета.
    • Заменяет два дефиса в любом имени KeyDelimiterна разделитель, используемый в конфигурации (обычно двоеточие). Azure Key Vault не разрешает двоеточие в именах секретов.
public class PrefixKeyVaultSecretManager : KeyVaultSecretManager
{
    private readonly string _prefix;

    public PrefixKeyVaultSecretManager(string prefix)
    {
        _prefix = $"{prefix}-";
    }

    public override bool Load(SecretProperties secret)
    {
        return secret.Name.StartsWith(_prefix);
    }

    public override string GetKey(KeyVaultSecret secret)
    {
        return secret.Name
            .Substring(_prefix.Length)
            .Replace("--", ConfigurationPath.KeyDelimiter);
    }
}

Метод Load вызывается алгоритмом поставщика, который выполняет итерацию по секретам хранилища, чтобы найти секреты, префиксированные версией. При обнаружении Loadпрефикса версии алгоритм использует GetKey метод для возврата имени конфигурации имени секрета. Он удаляет префикс версии из имени секрета. Остальная часть имени секрета возвращается для загрузки в пары "Имя-значение" конфигурации приложения.

При реализации этого подхода:

  1. Версия приложения, указанная в файле проекта приложения. В следующем примере для версии приложения задано 5.0.0.0значение :

    <PropertyGroup>
      <Version>5.0.0.0</Version>
    </PropertyGroup>
    
  2. Убедитесь, что <UserSecretsId> свойство присутствует в файле проекта приложения, где {GUID} указан guid, предоставленный пользователем:

    <PropertyGroup>
      <UserSecretsId>{GUID}</UserSecretsId>
    </PropertyGroup>
    

    Сохраните следующие секреты локально с помощью диспетчера секретов:

    dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev"
    dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
    
  3. Секреты сохраняются в Azure Key Vault с помощью следующих команд Azure CLI:

    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod"
    az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
    
  4. При запуске приложения загружаются секреты Key Vault. Строковый секрет 5000-AppSecret соответствует версии приложения, указанной в файле проекта приложения (5.0.0.0).

  5. Версия ( 5000 с дефисом) удаляется из имени ключа. В приложении конфигурация чтения с ключом AppSecret загружает значение секрета.

  6. Если версия приложения изменяется в файле 5.1.0.0 проекта и выполняется снова, возвращается 5.1.0.0_secret_value_dev значение секрета в среде разработки и 5.1.0.0_secret_value_prod в рабочей среде.

Примечание.

Вы также можете предоставить собственную SecretClient реализацию AddAzureKeyVault. Настраиваемый клиент разрешает общий доступ к одному экземпляру клиента в приложении.

Привязка массива к классу

Поставщик может считывать значения конфигурации в массив для привязки к массиву POCO.

При чтении из источника конфигурации, позволяющего ключам содержать разделители двоеточий (:) числовой сегмент ключа используется для различения ключей, составляющих массив (:0:, :1:... :{n}:). Дополнительные сведения см. в разделе "Конфигурация: привязка массива к классу".

Ключи Azure Key Vault не могут использовать двоеточие в качестве разделителя. Описанный в этой статье подход использует двойные дефисы (--) в качестве разделителя для иерархических значений (разделов). Ключи массива хранятся в Azure Key Vault с двойными дефисами и числовыми сегментами ключей (--0--, --1--... --{n}--).

Изучите следующую конфигурацию поставщика ведения журнала Serilog , предоставляемую ON-файлом JS. В массиве определены WriteTo два литерала объектов, которые отражают два приемника Serilog, описывающие назначения для выходных данных ведения журнала:

"Serilog": {
  "WriteTo": [
    {
      "Name": "AzureTableStorage",
      "Args": {
        "storageTableName": "logs",
        "connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
      }
    },
    {
      "Name": "AzureDocumentDB",
      "Args": {
        "endpointUrl": "https://contoso.documents.azure.com:443",
        "authorizationKey": "Eby8...GMGw=="
      }
    }
  ]
}

Конфигурация, показанная в предыдущем JSON-файле, хранится в Azure Key Vault с помощью двойного дефиса (--) нотации и числовых сегментов:

Ключ Значение
Serilog--WriteTo--0--Name AzureTableStorage
Serilog--WriteTo--0--Args--storageTableName logs
Serilog--WriteTo--0--Args--connectionString DefaultEnd...ountKey=Eby8...GMGw==
Serilog--WriteTo--1--Name AzureDocumentDB
Serilog--WriteTo--1--Args--endpointUrl https://contoso.documents.azure.com:443
Serilog--WriteTo--1--Args--authorizationKey Eby8...GMGw==

Перезагрузить секреты

Секреты кэшируются до IConfigurationRoot.Reload вызова. Впоследствии отключенные или обновленные секреты в хранилище не учитываются приложением, пока не Reload будет выполнено.

Configuration.Reload();

Отключенные и истекшие секреты

Секреты с истекшим сроком действия включаются по умолчанию в поставщик конфигурации. Чтобы исключить значения этих секретов в конфигурации приложения, обновите истекший срок действия или укажите конфигурацию с помощью пользовательского поставщика конфигурации:

class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
  public override bool Load(SecretProperties properties) =>
    properties.ExpiresOn.HasValue &&
    properties.ExpiresOn.Value > DateTimeOffset.Now;
}

Передайте этот пользовательский KeyVaultSecretManager код в AddAzureKeyVault:

// using Azure.Extensions.AspNetCore.Configuration.Secrets;

config.AddAzureKeyVault(
    new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
    new DefaultAzureCredential(),
    new SampleKeyVaultSecretManager());

Отключенные секреты не могут быть извлечены из Key Vault и никогда не включены.

Устранение неполадок

Если приложению не удается загрузить конфигурацию с помощью поставщика, сообщение об ошибке записывается в инфраструктуру ASP.NET core Logging. Следующие условия препятствуют загрузке конфигурации:

  • Приложение или сертификат настроены неправильно в Azure AD.
  • Хранилище не существует в Azure Key Vault.
  • Приложение не авторизовано для доступа к хранилищу.
  • Политика доступа не включает Get и List разрешения.
  • В хранилище данные конфигурации (пара "имя-значение") неправильно называются, отсутствуют или отключены.
  • Приложение имеет неправильное имя Key Vault (KeyVaultName), идентификатор приложения Azure AD (AzureADApplicationId), или отпечаток сертификата Azure AD (AzureADCertThumbprint), или идентификатор azure AD Directory (AzureADDirectoryId).
  • При добавлении политики доступа Key Vault для приложения была создана политика, но кнопка "Сохранить " не была выбрана в пользовательском интерфейсе политик доступа.

Дополнительные ресурсы