Ejercicio: acceso a los secretos almacenados en Azure Key Vault

Completado

Ya sabe que al habilitar identidades administradas para recursos de Azure crea una identidad para que la aplicación la use para la autenticación. Ahora, cree una aplicación que use esa identidad para acceder a secretos en el almacén.

Lectura de secretos de una aplicación ASP.NET Core

La API Azure Key Vault es una API REST que controla por completo la administración y el uso de claves y almacenes. Cada secreto de un almacén tiene una dirección URL única. Los valores de los secretos se recuperan con solicitudes HTTP GET.

El cliente oficial de Key Vault para .NET Core es la clase SecretClient del paquete NuGet Azure.Security.KeyVault.Secrets. Sin embargo, no es necesario usarla directamente. Con el método AddAzureKeyVault de ASP.NET Core, puede cargar todos los secretos de un almacén a Configuration API durante el inicio. Esta técnica le permite acceder a todos los secretos por nombre con la misma interfaz IConfiguration que usa para el resto de la configuración. Las aplicaciones que usan AddAzureKeyVault requieren los permisos Get y List para el almacén.

Sugerencia

Con independencia de la plataforma o lenguaje que use para compilar la aplicación, debe diseñarla para almacenar en caché los valores de secreto localmente o cargarlos en memoria durante el inicio, a menos que tenga una razón concreta para no hacerlo. Leerlos directamente desde el almacén cada vez que los necesite es un proceso innecesariamente lento y caro.

AddAzureKeyVault solo requiere el nombre del almacén como entrada, que se obtiene de la configuración de la aplicación local. También controla automáticamente la autenticación de la identidad administrada. Cuando se usa en una aplicación implementada en Azure App Service con identidades administradas de recursos de Azure habilitadas, se detecta el servicio de token de identidades administradas y se usa para la autenticación. Es una buena elección para la mayoría de los escenarios e implementa todos los procedimientos recomendados. Se usa en el ejercicio de esta unidad.

Lectura de los secretos de una aplicación Node.js

La API Azure Key Vault es una API REST que controla por completo la administración y el uso de claves y almacenes. Cada secreto de un almacén tiene una dirección URL única. Los valores de los secretos se recuperan con solicitudes HTTP GET.

El cliente oficial de Key Vault para aplicaciones de Node.js es la clase SecretClient del paquete de npm @azure/keyvault-secrets. Las aplicaciones que incluyen nombres de secreto en su configuración o código usan, por lo general, su método getSecret, que carga un valor de secreto según su nombre. getSecret requiere la identidad de la aplicación para tener el permiso Get en el almacén. Las aplicaciones diseñadas para cargar todos los secretos de un almacén también utilizan el método listPropertiesOfSecrets, que carga una lista de secretos y requiere el permiso List.

Para que la aplicación pueda crear una instancia de SecretClient, debe obtener un objeto de credencial para autenticarse en el almacén. Para realizar la autenticación, use el objeto DefaultAzureCredential proporcionado por el paquete npm @azure/identity. DefaultAzureCredential es adecuado para la mayoría de los escenarios en los que la aplicación está pensada para ejecutarse en última instancia en la nube de Azure porque DefaultAzureCredential combina las credenciales que se usan habitualmente para autenticarse cuando se implementan, con credenciales usadas para autenticarse en un entorno de desarrollo. DefaultAzureCredential intenta autenticarse mediante los siguientes mecanismos en orden:

  • Entorno DefaultAzureCredential lee la información de la cuenta especificada mediante variables de entorno y la usa para la autenticación.
  • Identidad administrada. Si la aplicación se implementa en un host de Azure con la identidad administrada habilitada, DefaultAzureCredential se autentica con esa cuenta.
  • Visual Studio Code. Si el desarrollador se ha autenticado mediante el complemento de la cuenta de Azure de Visual Studio Code, DefaultAzureCredential se autentica con esa cuenta.
  • Azure CLI. Si el desarrollador ha autenticado una cuenta mediante el comando az login de la CLI de Azure, DefaultAzureCredential se autentica con esa cuenta.

Para obtener más información, consulte la documentación.

Sugerencia

Con independencia de la plataforma o lenguaje que use para compilar la aplicación, debe diseñarla para almacenar en caché los valores de secreto localmente o cargarlos en memoria durante el inicio, a menos que tenga una razón concreta para no hacerlo. Leerlos directamente desde el almacén cada vez que los necesite es un proceso innecesariamente lento y caro.

Administración de secretos en una aplicación

Una vez cargado un secreto en la aplicación, depende de la aplicación que su administración sea segura. En la aplicación que se crea en este módulo, el valor del secreto se escribe en la respuesta del cliente y, para demostrar que se cargó correctamente, se visualiza en un explorador web. Devolver el valor de un secreto al cliente no es algo que normalmente haga. Por lo general, los secretos se utilizan para hacer cosas como inicializar las bibliotecas cliente para bases de datos o API remotas.

Importante

Revise siempre cuidadosamente el código para asegurarse de que la aplicación no escribe nunca secretos en cualquier tipo de salida, incluidos los registros, el almacenamiento y las respuestas.

Ejercicio

Para cargar el secreto desde el almacén, cree una nueva API web de ASP.NET Core y utilice AddAzureKeyVault.

Creación de la aplicación

  1. Para crear una aplicación de API web de ASP.NET Core y abrirla en el editor, ejecute el siguiente comando en Azure Cloud Shell.

    dotnet new webapi -o KeyVaultDemoApp
    cd KeyVaultDemoApp
    code .
    
  2. Después de que se cargue el editor, agregue el paquete NuGet que contiene AddAzureKeyVault y restaure todas las dependencias de la aplicación. En Azure Cloud Shell, ejecute estos comandos.

    dotnet add package Azure.Identity
    dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
    dotnet restore
    

Incorporación de código para cargar y usar secretos

Para demostrar el uso correcto de Key Vault, modifique la aplicación para cargar los secretos desde el almacén durante el inicio. También agregará un nuevo controlador con un punto de conexión que obtiene el secreto SecretPassword del almacén.

  1. Para el inicio de la aplicación, escriba el comando siguiente a fin de iniciar el editor.

    code .
    
  2. Abra Program.cs, elimine el contenido y reemplácelo por el siguiente código.

    using System;
    using Azure.Identity;
    using Microsoft.AspNetCore;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Hosting;
    
    namespace KeyVaultDemoApp
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                    })
                    .ConfigureAppConfiguration((context, config) =>
                    {
                        // Build the current set of configuration to load values from
                        // JSON files and environment variables, including VaultName.
                        var builtConfig = config.Build();
    
                        // Use VaultName from the configuration to create the full vault URI.
                        var vaultName = builtConfig["VaultName"];
                        Uri vaultUri = new Uri($"https://{vaultName}.vault.azure.net/");
    
                        // Load all secrets from the vault into configuration. This will automatically
                        // authenticate to the vault using a managed identity. If a managed identity
                        // is not available, it will check if Visual Studio and/or the Azure CLI are
                        // installed locally and see if they are configured with credentials that can
                        // access the vault.
                        config.AddAzureKeyVault(vaultUri, new DefaultAzureCredential());
                    });
        }
    }
    

    Importante

    Asegúrese de guardar los archivos cuando haya terminado de editarlos. Puede guardar archivos con el menú "..." o la tecla de acelerador (Ctrl+S en Windows y Linux, Cmd+S en macOS).

    El único cambio del código de inicio es la adición de ConfigureAppConfiguration. En este elemento, cargamos el nombre del almacén desde la configuración y llamamos a AddAzureKeyVault con él.

  3. Para el controlador, cree un archivo denominado SecretTestController.cs en la carpeta Controllers y pegue el código siguiente.

    Sugerencia

    Para crear un archivo, use el comando touch en Cloud Shell. En este caso, ejecute el comando touch Controllers/SecretTestController.cs. Para encontrar el archivo en la esquina superior derecha del panel Archivos del editor, seleccione el icono Actualizar.

    using System;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    
    namespace KeyVaultDemoApp.Controllers
    {
        [Route("api/[controller]")]
        public class SecretTestController : ControllerBase
        {
            private readonly IConfiguration _configuration;
    
            public SecretTestController(IConfiguration configuration)
            {
                _configuration = configuration;
            }
    
            [HttpGet]
            public IActionResult Get()
            {
                // Get the secret value from configuration. This can be done anywhere
                // we have access to IConfiguration. This does not call the Key Vault
                // API, because the secrets were loaded at startup.
                var secretName = "SecretPassword";
                var secretValue = _configuration[secretName];
    
                if (secretValue == null)
                {
                    return StatusCode(
                        StatusCodes.Status500InternalServerError,
                        $"Error: No secret named {secretName} was found...");
                }
                else {
                    return Content($"Secret value: {secretValue}" +
                        Environment.NewLine + Environment.NewLine +
                        "This is for testing only! Never output a secret " +
                        "to a response or anywhere else in a real app!");
                }
            }
        }
    }
    
  4. Ejecute el comando dotnet build en Azure Cloud Shell para asegurarse de que todo se compila correctamente. La aplicación está lista para ejecutarse. ¡Ahora es el momento de entrar en Azure!

Cree una nueva API web con Express.js y utilice los paquetes @azure/keyvault-secrets y @azure/identity para cargar el secreto desde el almacén.

Creación de la aplicación

Ejecute el siguiente código en Azure Cloud Shell para inicializar una nueva aplicación de Node.js, instalar los paquetes necesarios y abrir un nuevo archivo en el editor.

mkdir KeyVaultDemoApp
cd KeyVaultDemoApp
npm init -y
npm install @azure/identity @azure/keyvault-secrets express
touch app.js
code app.js

Incorporación de código para cargar y usar secretos

Para demostrar el uso correcto de Key Vault, la aplicación carga los secretos del almacén durante el inicio. Para demostrar que se han cargado los secretos, cree un punto de conexión que muestre el valor del secreto SecretPassword.

  1. Para configurar la aplicación, pegue el código siguiente en el editor. Este código importa los paquetes necesarios, establece la configuración de puerto e identificador URI del almacén, y crea un nuevo objeto para contener los nombres y valores del secreto.

    // Importing dependencies
    const { DefaultAzureCredential } = require("@azure/identity");
    const { SecretClient } = require("@azure/keyvault-secrets");
    const app = require('express')();
    
    // Initialize port
    const port = process.env.PORT || 3000;
    
    // Create Vault URI from App Settings
    const vaultUri = `https://${process.env.VaultName}.vault.azure.net/`;
    
    // Map of key vault secret names to values
    let vaultSecretsMap = {};
    

    Importante

    Asegúrese de guardar archivos mientras trabaja en ellos, especialmente cuando haya terminado. Puede guardar archivos con el menú "..." o la tecla de acelerador (Ctrl+S en Windows y Linux, Cmd+S en macOS).

  2. A continuación,agregue el código para autenticarse en el almacén y cargar los secretos. Este código se agrega como dos funciones independientes. Inserte un par de líneas en blanco después del código que ha agregado antes y, después, pegue el código siguiente.

    const getKeyVaultSecrets = async () => {
      // Create a key vault secret client
      let secretClient = new SecretClient(vaultUri, new DefaultAzureCredential());
      try {
        // Iterate through each secret in the vault
        listPropertiesOfSecrets = secretClient.listPropertiesOfSecrets();
        while (true) {
          let { done, value } = await listPropertiesOfSecrets.next();
          if (done) {
            break;
          }
          // Only load enabled secrets - getSecret will return an error for disabled secrets
          if (value.enabled) {
            const secret = await secretClient.getSecret(value.name);
            vaultSecretsMap[value.name] = secret.value;
          }
        }
      } catch(err) {
        console.log(err.message)
      }
    }
    
  3. Para comprobar si se ha cargado nuestro secreto, cree el punto de conexión de Express. Pegue este código.

    app.get('/api/SecretTest', (req, res) => {
      let secretName = 'SecretPassword';
      let response;
      if (secretName in vaultSecretsMap) {
        response = `Secret value: ${vaultSecretsMap[secretName]}\n\nThis is for testing only! Never output a secret to a response or anywhere else in a real app!`;
      } else {
        response = `Error: No secret named ${secretName} was found...`
      }
      res.type('text');
      res.send(response);
    });
    
  4. Llame a las funciones para cargar los secretos desde el almacén y, a continuación, inicie la aplicación. Pegue este último fragmento de código para completar la aplicación.

    (async () =>  {
      await getKeyVaultSecrets();
      app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
      });
    })().catch(err => console.log(err));
    
  5. Ha terminado de escribir código, así que asegúrese de guardar el archivo.

La aplicación está lista para ejecutarse. ¡Ahora es el momento de entrar en Azure!