Поделиться через


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

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

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

Packages

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

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

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

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

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

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

Хранилище секретов данных Development в среде

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

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

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

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

Диспетчер секретов используется из командной оболочки, открытой в корневом каталоге содержимого проекта, где — это имя, а — значение.

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"

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

Хранилище секретов в среде Production с 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, загружаемых в среду Development из 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 Key Vault и приложение для использования идентификатора приложения Microsoft Entra ID и сертификата X.509 для проверки подлинности в хранилище при размещении приложения за пределами Azure. См. дополнительные сведения о ключах, секретах и сертификатах.

Note

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

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

  1. Создайте сертификат архива PKCS#12 (PFX). Параметры создания сертификатов включают New-SelfSignedCertificate в Windows и OpenSSL.
  2. Установите сертификат в личное хранилище сертификатов текущего пользователя. Маркировка ключа как экспортируемого является необязательным. Обратите внимание на отпечаток сертификата, который используется позже в этом процессе.
  3. Экспортируйте сертификат архива PKCS#12 (PFX) в виде сертификата в кодировке DER (.cer).
  4. Зарегистрируйте приложение с помощью идентификатора Microsoft Entra (Регистрация приложений).
  5. Загрузите сертификат в кодировке DER (.cer) на Microsoft Entra ID.
    1. Выберите приложение в идентификаторе Microsoft Entra.
    2. Перейдите к сертификатам и секретам.
    3. Выберите " Отправить сертификат", чтобы отправить сертификат , содержащий открытый ключ. Допустим сертификат .cer, .pem или .crt.
  6. Сохраните имя Key Vault, идентификатор приложения и отпечаток сертификата в файле приложения appsettings.json .
  7. Перейдите в Хранилища ключей в портале Azure.
  8. Выберите хранилище ключей, которое вы создали в разделе «Хранение секретов в среде Production с помощью Azure Key Vault».
  9. Выберите Политики доступа.
  10. Выберите Добавить политику доступа.
  11. Откройте разрешения для секретов и предоставьте приложению разрешения Get и 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();

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

  • Имя хранилища ключей: contosovault
  • ИД приложения: 00001111-aaaa-2222-bbbb-3333cccc4444
  • Отпечаток сертификата: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:

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

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

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

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

В приложении-примере используется управляемое удостоверение, назначаемое системой, если директива препроцессора #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());
}

Note

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

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

appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

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

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

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

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

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

Если появится ошибка Access denied, убедитесь, что приложение зарегистрировано с помощью Microsoft Entra ID и ему предоставлен доступ к хранилищу. Убедитесь, что вы перезагрузили службу в 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 содержит следующие свойства:

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

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

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

Warning

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

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

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

В следующем примере секрет создаётся в Key Vault (и с помощью Secret Manager для среды Development) для 5000-AppSecret (знаки препинания не допускаются в именах секретов Key Vault). Этот секрет представляет собой секрет приложения версии 5.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 в Development среде и 5.1.0.0_secret_value_prod в Production.

Note

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

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

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

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

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

Изучите следующую конфигурацию поставщика ведения журнала Serilog , предоставляемую JSON-файлом. В массиве определены два литерала объектов 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 с помощью двойного дефиса (--) нотации и числовых сегментов:

Key Value
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 и никогда не учитываются.

Note

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

Troubleshoot

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

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

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

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

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

Packages

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

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

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

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

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

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

Хранилище секретов данных Development в среде

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

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

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

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

Диспетчер секретов используется из командной оболочки, открытой в корневом каталоге содержимого проекта, где — это имя, а — значение.

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"

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

Хранилище секретов в среде Production с 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, загружаемых в среду Development из 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 Key Vault и приложение для использования идентификатора приложения Microsoft Entra ID и сертификата X.509 для проверки подлинности в хранилище при размещении приложения за пределами Azure. См. дополнительные сведения о ключах, секретах и сертификатах.

Note

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

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

  1. Создайте сертификат архива PKCS#12 (PFX). Параметры создания сертификатов включают New-SelfSignedCertificate в Windows и OpenSSL.
  2. Установите сертификат в личное хранилище сертификатов текущего пользователя. Маркировка ключа как экспортируемого является необязательным. Обратите внимание на отпечаток сертификата, который используется позже в этом процессе.
  3. Экспортируйте сертификат архива PKCS#12 (PFX) в виде сертификата в кодировке DER (.cer).
  4. Зарегистрируйте приложение с помощью идентификатора Microsoft Entra (Регистрация приложений).
  5. Загрузите сертификат в кодировке DER (.cer) на Microsoft Entra ID.
    1. Выберите приложение в идентификаторе Microsoft Entra.
    2. Перейдите к сертификатам и секретам.
    3. Выберите " Отправить сертификат", чтобы отправить сертификат , содержащий открытый ключ. Допустим сертификат .cer, .pem или .crt.
  6. Сохраните имя Key Vault, идентификатор приложения и отпечаток сертификата в файле приложения appsettings.json .
  7. Перейдите в Хранилища ключей в портале Azure.
  8. Выберите хранилище ключей, которое вы создали в разделе «Хранение секретов в среде Production с помощью Azure Key Vault».
  9. Выберите Политики доступа.
  10. Выберите Добавить политику доступа.
  11. Откройте разрешения для секретов и предоставьте приложению разрешения Get и 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>());

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

  • Имя хранилища ключей: contosovault
  • ИД приложения: 00001111-aaaa-2222-bbbb-3333cccc4444
  • Отпечаток сертификата: fe14593dd66b2406c5269d742d04b6e1ab03adb1

appsettings.json:

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

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

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

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

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

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

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

Приложение, развернутое в службе приложение Azure, автоматически регистрируется с идентификатором Microsoft Entra при создании службы. Получите идентификатор объекта из развертывания для использования в следующей команде. Идентификатор объекта отображается в портале 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.
  • Создается новый SecretClient с использованием экземпляра DefaultAzureCredential.
  • Экземпляр 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>());

Note

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

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

appsettings.json:

{
  "KeyVaultName": "Key Vault Name"
}

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

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

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

Параметры конфигурации

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

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

Note

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

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

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

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

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

Warning

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

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

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

В следующем примере секрет создаётся в Key Vault (и с помощью Secret Manager для среды Development) для 5000-AppSecret (знаки препинания не допускаются в именах секретов Key Vault). Этот секрет представляет собой секрет приложения версии 5.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 в Development среде и 5.1.0.0_secret_value_prod в Production.

Note

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

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

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

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

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

Изучите следующую конфигурацию поставщика ведения журнала Serilog , предоставляемую JSON-файлом. В массиве определены два литерала объектов 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 с помощью двойного дефиса (--) нотации и числовых сегментов:

Key Value
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 и никогда не учитываются.

Note

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

Troubleshoot

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

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

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