Использование ссылок на Key Vault в Службе приложений и Функциях Azure

В этой статье показано, как работать с секретами из Azure Key Vault в приложении Службы приложений или Функций Azure без каких-либо изменений кода. Служба Azure Key Vault обеспечивает централизованное управление секретами и полный контроль над политиками доступа и журналами аудита.

Предоставление приложению доступа к Key Vault

Чтобы просматривать секреты из Key Vault, необходимо создать хранилище и предоставить приложению разрешения на доступ к нему.

  1. Чтобы создать хранилище ключей, изучите краткое руководство по Key Vault.

  2. Создайте для приложения управляемое удостоверение.

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

  3. Создайте политику доступа в Key Vault для созданного ранее удостоверения приложения. Включите в этой политике разрешения "Get" на получение секретов. Не устанавливайте "авторизованное приложение" или параметр applicationId, так как он не совместим с управляемым удостоверением.

Хранилища с ограниченным доступом из сети

Если в хранилище настроены ограничения сети, необходимо также убедиться, что у приложения есть доступ к сети.

  1. Убедитесь, что в приложении настроены исходящие сетевые возможности, как описано в статье Сетевые функции Службы приложений и Параметры сети для Функций Azure.

    Приложениям Linux, пытающимся использовать частные конечные точки, требуется, чтобы приложение было настроено явным образом, а весь трафик направлялся через виртуальную сеть. В будущем обновлении это требование будет отменено. Чтобы сделать это, выполните следующую команду Azure CLI или Azure PowerShell:

    az webapp config set --subscription <sub> -g MyResourceGroupName -n MyAppName --generic-configurations '{"vnetRouteAllEnabled": true}'
    
  2. Убедитесь, что в конфигурации хранилища учитывается сеть или подсеть, через которую приложение будет получать к нему доступ.

Примечание

Контейнер Windows в настоящее время не поддерживает ссылки Key Vault через интеграцию виртуальной сети.

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

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

После предоставления разрешений для удостоверения, назначаемого пользователем, выполните следующие действия.

  1. Назначьте это удостоверение8 приложению, если вы этого еще не сделали.

  2. Настройте приложение для использования этого удостоверения для операций со ссылками Key Vault, задав для свойства keyVaultReferenceIdentity идентификатор ресурса удостоверения, назначаемого пользователем.

    userAssignedIdentityResourceId=$(az identity show -g MyResourceGroupName -n MyUserAssignedIdentityName --query id -o tsv)
    appResourceId=$(az webapp show -g MyResourceGroupName -n MyAppName --query id -o tsv)
    az rest --method PATCH --uri "${appResourceId}?api-version=2021-01-01" --body "{'properties':{'keyVaultReferenceIdentity':'${userAssignedIdentityResourceId}'}}"
    

Эта конфигурация будет применяться ко всем ссылкам для приложения.

Синтаксис ссылок

Ссылка на Key Vault имеет вид @Microsoft.KeyVault({referenceString}), где {referenceString} заменяется одним из следующих значений.

Строка ссылки Описание
SecretUri=secretUri Здесь SecretUri является полным URI плоскости данных секрета в Key Vault, дополнительно включая версию, например https://myvault.vault.azure.net/secrets/mysecret/ или https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931.
VaultName=vaultName;SecretName=secretName;SecretVersion=secretVersion VaultName — обязательное имя ресурса Key Vault. SecretName — обязательное имя целевого секрета. SecretVersion — (необязательно) при наличии указывает используемую версию секрета.

Вот пример допустимой полной ссылки:

@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/)

Еще один вариант:

@Microsoft.KeyVault(VaultName=myvault;SecretName=mysecret)

Поворот

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

Получение параметров приложения из Key Vault

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

Чтобы использовать ссылку на Key Vault в качестве параметра приложения, укажите эту ссылку в значении этого параметра. Приложения смогут использовать секрет через ключ параметра, как обычно. Изменения кода не требуются.

Совет

Большинство параметров приложения, выраженных в виде ссылок на Key Vault, следует пометить как параметры слота, так как для каждой среды должны быть настроены разные хранилища.

Рекомендации по подключению службы "Файлы Azure"

Приложения могут использовать параметр WEBSITE_CONTENTAZUREFILECONNECTIONSTRING приложения для подключения службы "Файлы Azure" в качестве файловой системы. Этот параметр содержит дополнительные проверки, позволяющие убедиться, что приложение можно запустить надлежащим образом. Платформа использует общую папку содержимого в службе "Файлы Azure" и принимает имя по умолчанию, если не указано иное с помощью параметра WEBSITE_CONTENTSHARE. Для любых запросов, которые изменяют эти параметры, платформа попытается проверить, существует ли общая папка содержимого, и создать ее, если не существует. Если разместить или создать общую папку содержимого не удается, запрос блокируется.

При использовании ссылок на Key Vault для этого параметра проверка по умолчанию завершится сбоем, так как сам секрет нельзя разрешить во время обработки входящего запроса. Чтобы избежать этой проблемы, можно пропустить проверку, задав для параметра WEBSITE_SKIP_CONTENTSHARE_VALIDATION значение 1. Это поможет обойти все проверки, и общая папка содержимого не будет создана. Ее следует создать заранее.

Внимание!

Если проверка пропущена и либо строка подключения, либо общая папка содержимого являются недопустимыми, приложение не сможет правильно запуститься и будет обслуживать только ошибки HTTP 500.

В рамках создания сайта возможно также, что попытка подключения общей папки содержимого завершается сбоем из-за того, что не распространены разрешения управляемого удостоверения или не настроена интеграция виртуальной сети. Вы можете отложить настройку службы "Файлы Azure" до последующего использования в шаблоне развертывания. Дополнительные сведения см. в статье Развертывание Azure Resource Manager. Служба приложений будет использовать файловую систему по умолчанию до тех пор, пока не будет настроена служба "Файлы" и не будут скопированы файлы, поэтому предпринимать попытки развертывания в течение промежуточного периода времени до подключения службы "Файлы Azure" не следует.

Развертывание Azure Resource Manager

При автоматизации развертывания ресурсов с помощью шаблонов Azure Resource Manager вам может потребоваться определенный порядок размещения зависимостей, без которого эта функция не будет работать. Важно также отметить, что параметры приложения должны быть определены как собственный ресурс, без свойства siteConfig в определении сайта. Это связано с тем, что сначала нужно определить сайт и получить для него назначаемый системой идентификатор, и лишь затем можно применить этот идентификатор в политике доступа.

В качестве примера давайте рассмотрим следующий псевдошаблон приложения-функции:

{
    //...
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "name": "[variables('storageAccountName')]",
            //...
        },
        {
            "type": "Microsoft.Insights/components",
            "name": "[variables('appInsightsName')]",
            //...
        },
        {
            "type": "Microsoft.Web/sites",
            "name": "[variables('functionAppName')]",
            "identity": {
                "type": "SystemAssigned"
            },
            //...
            "resources": [
                {
                    "type": "config",
                    "name": "appsettings",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('storageConnectionStringName'))]",
                        "[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('appInsightsKeyName'))]"
                    ],
                    "properties": {
                        "AzureWebJobsStorage": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
                        "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
                        "APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyResourceId')).secretUriWithVersion, ')')]",
                        "WEBSITE_ENABLE_SYNC_UPDATE_SITE": "true"
                        //...
                    }
                },
                {
                    "type": "sourcecontrols",
                    "name": "web",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
                        "[resourceId('Microsoft.Web/sites/config', variables('functionAppName'), 'appsettings')]"
                    ],
                }
            ]
        },
        {
            "type": "Microsoft.KeyVault/vaults",
            "name": "[variables('keyVaultName')]",
            //...
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
            ],
            "properties": {
                //...
                "accessPolicies": [
                    {
                        "tenantId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.tenantId]",
                        "objectId": "[reference(resourceId('Microsoft.Web/sites/', variables('functionAppName')), '2020-12-01', 'Full').identity.principalId]",
                        "permissions": {
                            "secrets": [ "get" ]
                        }
                    }
                ]
            },
            "resources": [
                {
                    "type": "secrets",
                    "name": "[variables('storageConnectionStringName')]",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
                    ],
                    "properties": {
                        "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),'2019-09-01').key1)]"
                    }
                },
                {
                    "type": "secrets",
                    "name": "[variables('appInsightsKeyName')]",
                    //...
                    "dependsOn": [
                        "[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
                        "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
                    ],
                    "properties": {
                        "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2019-09-01').InstrumentationKey]"
                    }
                }
            ]
        }
    ]
}

Примечание

В этом примере развертывание системы управления версиями зависит от параметров приложения. Такое поведение обычно считается небезопасным, так как обновление параметров приложения выполняется асинхронно. Но в этом примере оно будет синхронным, так как мы добавили параметр приложения WEBSITE_ENABLE_SYNC_UPDATE_SITE. Это означает, что развертывания системы управления версиями начнется только после того, как завершится обновление параметров приложения. Дополнительные параметры приложения описаны в разделе Переменные среды и параметры приложения в службе приложений Azure.

Устранение неполадок со ссылками на Key Vault

Если ссылка не разрешается должным образом, вместо нее будет использоваться значение ссылки. Это означает, что для параметров приложения будет создана переменная среды со значением, имеющим синтаксис @Microsoft.KeyVault(...). Из-за этого приложение может выдать ошибки, так как оно ожидало секрет определенной структуры.

Чаще всего это происходит из-за неправильной настройки политики доступа к Key Vault. Однако это также может быть вызвано тем, что секрет больше не существует или есть синтаксическая ошибка в самой ссылке.

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

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

Использование детектора для Службы приложений

  1. На портале перейдите к вашему приложению.
  2. Выберите колонку Диагностика и решение проблем.
  3. Выберите Availability and Performance (Доступность и производительность), а затем — Web app down (Отключение веб-приложений).
  4. Найдите Key Vault Application Settings Diagnostics (Диагностика параметров приложения) и щелкните Дополнительные сведения.

Использование детектора для Функций Azure

  1. На портале перейдите к вашему приложению.
  2. Перейдите к Функциям платформы.
  3. Выберите колонку Диагностика и решение проблем.
  4. Выберите Availability and Performance (Доступность и производительность), а затем — Function app down or reporting errors (Приложение-функция не работает или уведомляет об ошибках).
  5. Щелкните Key Vault Application Settings Diagnostics (Диагностика параметров приложения).