Exercice : Accéder aux secrets stockés dans Azure Key Vault

Effectué

Vous savez comment activer les identités managées pour les ressources Azure crée une identité pour votre application à utiliser pour l’authentification. Créez maintenant une application qui utilise cette identité pour accéder aux secrets dans le coffre.

Lire des secrets dans une application ASP.NET Core

L’API Azure Key Vault est une API REST qui gère toute l’administration et l’utilisation des clés et des coffres. Chaque secret d’un coffre a une URL unique. Les valeurs secrètes sont récupérées avec des requêtes HTTP GET.

Le client Key Vault officiel pour .NET Core est la classe SecretClient dans le package NuGet Azure.Security.KeyVault.Secrets. Toutefois, vous n’avez pas besoin de l’utiliser directement. La méthode AddAzureKeyVault d’ASP.NET Core permet de charger tous les secrets d’un coffre dans l’API Configuration au démarrage. Cette technique vous permet d’accéder à tous vos secrets à l’aide de leur nom en utilisant la même interface IConfiguration que celle utilisée pour le reste de votre configuration. Les applications qui utilisent AddAzureKeyVault doivent avoir les autorisations Get et List sur le coffre.

Conseil

Quel que soit le framework ou le langage utilisé pour créer l’application, mettez en cache localement les valeurs de secrets ou chargez-les en mémoire au démarrage, sauf si vous avez une raison particulière de ne pas le faire. Il est inutilement lent et coûteux de les lire directement dans le coffre chaque fois que vous en avez besoin.

AddAzureKeyVault ne demande en entrée que le nom de coffre, que vous obtenez à partir de la configuration de votre application locale. Il gère aussi automatiquement l’authentification des identités managées. Utilisé dans une application déployée sur Azure App Service avec les identités managées des ressources Azure activées, il détecte le service de jetons des identités managées et l’utilise pour l’authentification. Il convient à la plupart des scénarios et implémente toutes les meilleures pratiques. Vous l’utilisez dans l’exercice de cette unité.

Lire des secrets dans une application Node.js

L’API Azure Key Vault est une API REST qui gère toute l’administration et l’utilisation des clés et des coffres. Chaque secret d’un coffre a une URL unique. Les valeurs secrètes sont récupérées avec des requêtes HTTP GET.

Le client Key Vault officiel des applications Node.js est la classe SecretClient du package npm @azure/keyvault-secrets. Les applications qui intègrent le nom des secrets dans leur configuration ou leur code utilisent généralement sa méthode getSecret, qui charge une valeur de secret pour son nom. Pour pouvoir utiliser getSecret, il faut que l’identité de l’application dispose de l’autorisation Get sur le coffre. Les applications conçues pour charger tous les secrets d’un coffre utilisent également la méthode listPropertiesOfSecrets, qui charge une liste de secrets et réclame l’autorisation List.

Pour que l’application puisse créer une instance SecretClient, elle doit au préalable récupérer un objet d’informations d’identification pour s’authentifier auprès du coffre. Pour vous authentifier, utilisez les DefaultAzureCredential fournis par le package npm @azure/identity. Le DefaultAzureCredential convient à la plupart des scénarios où l’application est destinée à s’exécuter éventuellement dans le cloud Azure, car le DefaultAzureCredential combine les informations d’identification couramment utilisées pour s’authentifier lors du déploiement, avec les informations d’identification utilisées pour s’authentifier dans un environnement de développement. Le DefaultAzureCredential tente de s’authentifier à l’aide des mécanismes suivants dans l’ordre :

  • L’environnement. Le DefaultAzureCredential lit les informations de compte spécifiées via des variables d’environnement et les utilisent pour l’authentification.
  • Identité managée. Si l’application est déployée sur un hôte Azure avec l’identité managée activée, le DefaultAzureCredential s’authentifie auprès de ce compte.
  • Visual Studio Code. Si le développeur s’est authentifié à l’aide du plug-in compte Azure Visual Studio Code, le DefaultAzureCredential s’authentifie avec ce compte.
  • Azure CLI. Si le développeur a authentifié un compte via la commande az login Azure CLI, le DefaultAzureCredential s’authentifie auprès de ce compte.

Pour plus d'informations, consultez la documentation.

Conseil

Quel que soit le framework ou le langage utilisé pour créer l’application, mettez en cache localement les valeurs de secrets ou chargez-les en mémoire au démarrage, sauf si vous avez une raison particulière de ne pas le faire. Il est inutilement lent et coûteux de les lire directement dans le coffre chaque fois que vous en avez besoin.

Gérer les secrets dans une application

Une fois qu’un secret est chargé dans votre application, il revient à celle-ci de le gérer de manière sécurisée. Dans l’application que vous générez dans ce module, vous écrivez la valeur de votre secret en réponse au client. Ensuite, pour montrer qu’elle a été chargée avec succès, vous l’affichez dans un navigateur web. Le renvoi d’une valeur de secret au client n’est pas une action « normale » ! En règle générale, des secrets sont utilisés pour effectuer des actions telles qu’initialiser des bibliothèques de client pour des bases de données ou des API distantes.

Important

Révisez toujours attentivement votre code pour vous assurer que votre application n’écrit jamais de secrets dans quelque type de sortie que ce soit, dont des journaux, du stockage et des réponses.

Exercice

Pour charger le secret à partir de notre coffre, vous créez une API web ASP.NET Core et utilisez AddAzureKeyVault.

Créer l’application

  1. Pour créer une application API web ASP.NET Core et l’ouvrir dans l’éditeur, exécutez les commandes suivantes dans Azure Cloud Shell.

    dotnet new webapi -o KeyVaultDemoApp
    cd KeyVaultDemoApp
    code .
    
  2. Après le chargement de l’éditeur, ajoutez le paquet NuGet contenant AddAzureKeyVault et restaurez toutes les dépendances de l’application. Dans Azure Cloud Shell, exécutez les commandes suivantes.

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

Ajouter du code pour charger et utiliser des secrets

Pour illustrer une utilisation correcte du coffre de clés, modifiez votre application pour charger les secrets à partir du coffre au démarrage. Vous ajoutez également un nouveau contrôleur avec un point de terminaison qui récupère votre secret SecretPassword auprès du coffre.

  1. Pour le démarrage de l’application, entrez la commande suivante pour lancer l’éditeur.

    code .
    
  2. Ouvrez Program.cs, supprimez le contenu et remplacez-le par le code suivant.

    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());
                    });
        }
    }
    

    Important

    N’oubliez pas d’enregistrer les fichiers quand vous avez fini de les modifier. Vous pouvez enregistrer des fichiers à l’aide du menu « ... » ou de la touche d’accès rapide (Ctrl+S sous Windows et linux, Cmd+S sous macOS).

    Le seul changement par rapport au code de démarrage est l’ajout de ConfigureAppConfiguration. C’est au niveau de cet élément que nous chargeons le nom du coffre à partir de la configuration et l’utilisons pour appeler AddAzureKeyVault.

  3. Pour le contrôleur, créez dans le dossier Controllers un nouveau fichier nommé SecretTestController.cs, puis collez-y le code suivant.

    Conseil

    Pour créer un fichier, utilisez la commande touch dans Cloud Shell. Dans ce cas, exécutez la commande touch Controllers/SecretTestController.cs. Pour l’afficher, en haut à droite du volet Fichiers de l’éditeur, sélectionnez l’icône Actualiser.

    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. Exécutez la commande dotnet build dans Azure Cloud Shell pour vous assurer que tout sera compilé. L’application est prête à s’exécuter. Maintenant, il est temps de la mettre dans Azure !

Créez une API web avec Express.js et utilisez les packages @azure/keyvault-secrets et @azure/identity pour charger le secret à partir de notre coffre.

Créer l'application

Exécutez le code suivant dans Azure Cloud Shell, pour initialiser une nouvelle application Node.js, installer les packages nécessaires et ouvrir un nouveau fichier dans l’éditeur.

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

Ajouter du code pour charger et utiliser des secrets

Pour illustrer une utilisation correcte de Key Vault, votre application charge des secrets à partir du coffre au démarrage. Pour illustrer que vos secrets sont chargés, créez un point de terminaison qui affiche la valeur du secret SecretPassword.

  1. Pour configurer l’application, collez le code suivant dans l’éditeur. Ce code importe les packages nécessaires, définit la configuration de l’URI du coffre et du port et crée un objet qui servira à stocker le nom et la valeur des secrets.

    // 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 = {};
    

    Important

    Veillez à enregistrer les fichiers au fur et à mesure, et surtout lorsque vous avez terminé. Vous pouvez enregistrer des fichiers à l’aide du menu « ... » ou de la touche d’accès rapide (Ctrl+S sous Windows et linux, Cmd+S sous macOS).

  2. Ensuite, ajoutez le code permettant de s’authentifier auprès du coffre et de charger les secrets. Vous ajoutez ce code en tant que deux fonctions distinctes. Insérez quelques lignes vides après le code ajouté précédemment, puis collez le code suivant.

    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. Pour tester si notre secret a été chargé, créez le point de terminaison Express. Collez ce code.

    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. Appelez vos fonctions pour charger les secrets à partir de notre coffre, puis démarrez l’application. Pour terminer l’application, collez ce dernier extrait de code.

    (async () =>  {
      await getKeyVaultSecrets();
      app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
      });
    })().catch(err => console.log(err));
    
  5. Vous avez terminé l’écriture du code. Veillez à bien enregistrer le fichier.

L’application est prête à s’exécuter. Maintenant, il est temps de la mettre dans Azure !