Uso de referencias de Key Vault como configuración de la aplicación en Azure App Service y Azure Functions

En este artículo se muestra cómo usar secretos de Azure Key Vault como valores de configuración de la aplicación o cadenas de conexión en las aplicaciones de App Service o Azure Functions.

Azure Key Vault es un servicio que proporciona administración centralizada de los secretos, con control total sobre las directivas de acceso y el historial de auditoría. Cuando una configuración de aplicación o una cadena de conexión es una referencia del almacén de claves, el código de la aplicación puede usarlo como cualquier otra configuración de aplicación o cadena de conexión. De este modo, puede mantener los secretos separados de la configuración de la aplicación. La configuración de la aplicación se cifra de forma segura en reposo, pero si necesita funcionalidades de administración de secretos, deben incluirse en un almacén de claves.

Conceder a la aplicación acceso a un almacén de claves

Para leer secretos desde un almacén de claves, debe tener creado un almacén y proporcionar a la aplicación permiso para acceder a él.

  1. Para crear un almacén de claves, siga la guía de inicio rápido de Key Vault.

  2. Cree una identidad administrada para la aplicación.

    Las referencias del almacén de claves usan la identidad asignada por el sistema de la aplicación de forma predeterminada, pero puede especificar una identidad asignada por el usuario.

  3. Autorice el acceso de lectura a los secretos del almacén de claves para la identidad administrada que creó anteriormente. La forma de hacerlo depende del modelo de permisos del almacén de claves:

Acceso a almacenes restringidos de red

Si el almacén está configurado con restricciones de red, asegúrese de que la aplicación tenga acceso a la red. Los almacenes no deberían depender de las direcciones IP de salida públicas de la aplicación porque la dirección IP de origen de la solicitud secreta podría ser diferente. En su lugar, el almacén debería configurarse para aceptar tráfico de una red virtual que use la aplicación.

  1. Asegúrese de que la aplicación tenga configuradas funcionalidades de red de salida, tal como se describe en Características de red de App Service y Opciones de red de Azure Functions.

    Las aplicaciones de Linux que se conectan a puntos de conexión privados deben configurarse explícitamente para enrutar todo el tráfico a través de la red virtual. Este requisito se quitará en una actualización futura. Para establecer esta configuración, ejecute el siguiente comando:

    az webapp config set --subscription <sub> -g <group-name> -n <app-name> --generic-configurations '{"vnetRouteAllEnabled": true}'
    
  2. Asegúrese de que la configuración del almacén permita a la red o la subred que la aplicación usa acceder al almacén.

Acceso a los almacenes con una identidad asignada por el usuario

Algunas aplicaciones necesitan hacer referencia a secretos en el momento de la creación, cuando una identidad asignada por el sistema aún no está disponible. En estos casos, se puede crear una identidad asignada por el usuario y concederle acceso al almacén de antemano.

Una vez que haya concedido permisos a la identidad asignada por el usuario, siga estos pasos:

  1. Asigne la identidad a la aplicación si aún no lo ha hecho.

  2. Configure la aplicación para que use esta identidad para las operaciones de referencia del almacén de claves estableciendo la propiedad keyVaultReferenceIdentity en el id. de recurso de la identidad asignada por el usuario.

    identityResourceId=$(az identity show --resource-group <group-name> --name <identity-name> --query id -o tsv)
    az webapp update --resource-group <group-name> --name <app-name> --set keyVaultReferenceIdentity=${identityResourceId}
    

Esta configuración se aplica a todas las referencias del almacén de claves para la aplicación.

Rotación

Si no se especifica la versión del secreto en la referencia, la aplicación usa la versión más reciente que exista en el almacén de claves. Cuando haya disponibles versiones más recientes, como con un evento de rotación, la aplicación se actualiza automáticamente y comienza a usar la versión más reciente en el plazo de 24 horas. El retraso se debe a App Service almacena en caché los valores de las referencias del almacén de claves y lo vuelve a capturar cada 24 horas. Cualquier cambio de configuración en la aplicación hace que la aplicación se reinicie y que se vuelvan a recuperar inmediatamente todos los secretos a los que se hace referencia.

Configuración de la aplicación de origen del almacén de claves

Para usar una referencia del almacén de claves, establezca la referencia como valor de la configuración. La aplicación puede hacer referencia al secreto mediante su clave de la forma habitual. No se requiere ningún cambio de código.

Sugerencia

La mayoría de los valores de configuración de la aplicación que usan referencias del almacén de claves se deben marcar como configuración de ranuras, así que debe tener distintos almacenes para cada entorno.

Una referencia del almacén de claves tiene el formato @Microsoft.KeyVault({referenceString}), donde {referenceString} tiene uno de los formatos siguientes:

Cadena de referencia Descripción
SecretUri=secretUri SecretUri debe ser el URI completo del plano de datos de un secreto en el almacén y, opcionalmente, puede incluir una versión; por ejemplo, https://myvault.vault.azure.net/secrets/mysecret/ o https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931
VaultName=vaultName;SecretName=secretName;SecretVersion=secretVersion VaultName es necesario y es el nombre del almacén. SecretName es obligatorio y es el nombre del secreto. SecretVersion es opcional, pero si está presente indica la versión del secreto que se usará.

Por ejemplo, una referencia completa se parecería a la cadena siguiente:

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

O bien:

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

Consideraciones para el montaje de Azure Files

Las aplicaciones pueden usar la configuración de la aplicación WEBSITE_CONTENTAZUREFILECONNECTIONSTRING para montar Azure Files como el sistema de archivos. Esta configuración tiene comprobaciones de validación para garantizar que la aplicación se puede iniciar correctamente. La plataforma se basa en tener un recurso compartido de contenido dentro de Azure Files y supone un nombre predeterminado a menos que se especifique uno a través de la configuración WEBSITE_CONTENTSHARE. Para las solicitudes que modifican esta configuración, la plataforma valida si existe este recurso compartido de contenido, e intenta crearlo si no existe. Si no se puede encontrar o crear el recurso compartido de contenido, bloquea la solicitud.

Cuando se usan referencias del almacén de claves en esta configuración, se produce un error en la comprobación de validación de forma predeterminada, ya que el secreto no se puede resolver mientras se procesa la solicitud entrante. Para evitar este problema, puede omitir la validación al establecer el valor de WEBSITE_SKIP_CONTENTSHARE_VALIDATION en "1". Esta configuración indica a App Service que omita todas las comprobaciones y no crea el recurso compartido de contenido automáticamente. Debe asegurarse de crearlo de antemano.

Precaución

Si omite la validación y, además, la cadena de conexión o el recurso compartido de contenido no son válidos, la aplicación no podrá iniciarse correctamente y solo generará errores HTTP 500.

Como parte de la creación de la aplicación, se podría generar un error al intentar montar el recurso compartido de contenido debido a que no se han propagado los permisos de identidad administrada, o a que no se ha configurado la integración de la red virtual. Puede aplazar la configuración de Azure Files hasta más adelante en la plantilla de implementación para realizar esto. Para más información, consulte Implementación de Azure Resource Manager. En este caso, App Service usa un sistema de archivos predeterminado hasta que se configura Azure Files, y los archivos no se copian. Debe asegurarse de que no se produzca ningún intento de implementación durante el período provisional antes de montar Azure Files.

Consideraciones sobre la instrumentación de Application Insights

Las aplicaciones pueden usar la configuración de la aplicación APPINSIGHTS_INSTRUMENTATIONKEY o APPLICATIONINSIGHTS_CONNECTION_STRING para integrarse con Application Insights. Las experiencias del portal para App Service y Azure Functions también usan esta configuración para exponer los datos de telemetría del recurso. Si se hace referencia a estos valores desde Key Vault, estas experiencias no están disponibles y, en su lugar, se debe trabajar directamente con el recurso de Application Insights para ver la telemetría. Sin embargo, estos valores no se consideran secretos, por lo que también puede considerar la posibilidad de configurarlos directamente en lugar de usar referencias del almacén de claves.

Implementación de Azure Resource Manager

Al automatizar las implementaciones de recursos mediante plantillas de Azure Resource Manager, puede que deba secuenciar sus dependencias en un orden determinado para que esta característica funcione. Asegúrese de definir la configuración de la aplicación como su propio recurso en lugar de usar una propiedad siteConfig en la definición de la aplicación. El motivo es que primero es necesario definir la aplicación para que la identidad asignada por el sistema se cree con ella y se pueda usar en la directiva de acceso.

La siguiente pseudoplantilla es un ejemplo de cómo podría ser una aplicación de funciones:

{
    //...
    "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('storageConnectionStringName')).secretUriWithVersion, ')')]",
                        "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringName')).secretUriWithVersion, ')')]",
                        "APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyName')).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]"
                    }
                }
            ]
        }
    ]
}

Nota

En este ejemplo, la implementación del control de código fuente depende de la configuración de la aplicación. Este comportamiento es normalmente poco seguro, ya que la actualización de la configuración de la aplicación se comporta de forma asincrónica. Sin embargo, como hemos incluido la configuración de la aplicación WEBSITE_ENABLE_SYNC_UPDATE_SITE, la actualización es sincrónica. Esto significa que la implementación del control de código fuente solo comenzará una vez que la configuración de la aplicación se haya actualizado completamente. Para conocer más opciones de configuración de la aplicación, vea Variables de entorno y configuración de la aplicación en Azure App Service.

Solución de problemas de las referencias del almacén de claves

Si una referencia no se resuelve correctamente, la cadena de referencia se usa en su lugar (por ejemplo, @Microsoft.KeyVault(...)). Puede hacer que la aplicación produzca errores, ya que espera un secreto de un valor diferente.

Habitualmente, el error al resolver se debe a una configuración incorrecta de la directiva de acceso de Key Vault. Sin embargo, también puede deberse a que el secreto ya no existe o a un error de sintaxis de la propia referencia.

Si la sintaxis es correcta, compruebe el estado de resolución actual en el portal para ver otras posibles causas del error. Vaya a Configuración de la aplicación y seleccione "Editar" para la referencia en cuestión. El cuadro de diálogo de edición muestra información de estado, incluidos los errores. Si no ve el mensaje de estado, significa que la sintaxis no es válida y no se reconoce como una referencia del almacén de claves.

También puede usar uno de los detectores integrados para obtener información adicional.

Uso del detector en App Service

  1. En el portal, vaya a la aplicación.
  2. Seleccione Diagnóstico y solución de problemas.
  3. Elija Disponibilidad y rendimiento y seleccione Aplicación web fuera de servicio.
  4. En el cuadro de búsqueda, busque y seleccione Diagnóstico de configuración de la aplicación de Key Vault.

Uso del detector en Azure Functions

  1. En el portal, vaya a la aplicación.
  2. Seleccione Características de la plataforma.
  3. Seleccione Diagnóstico y solución de problemas.
  4. Elija Disponibilidad y rendimiento y seleccione Aplicación de funciones fuera de servicio o notifica errores.
  5. Seleccione Diagnóstico de configuración de la aplicación de Key Vault.