Esercizio - Accedere ai segreti archiviati in Azure Key Vault

Completato

Si sa che l'abilitazione delle identità gestite per le risorse di Azure crea un'identità da usare per l'autenticazione dell'app. Creare ora un'app che usi tale identità per accedere ai segreti nell'insieme di credenziali.

Leggere i segreti in un'app ASP.NET Core

L'API Azure Key Vault è un'API REST che gestisce tutte le attività di gestione e utilizzo delle chiavi e degli insiemi di credenziali. Ogni segreto in un insieme di credenziali dispone di un URL univoco. I valori dei segreti vengono recuperati con le richieste HTTP GET.

Il client di Key Vault .NET Core ufficiale è la classe SecretClient nel pacchetto NuGet Azure.Security.KeyVault.Secrets. Tuttavia, non è necessario usarlo direttamente. Con il metodo AddAzureKeyVault di ASP.NET Core è possibile caricare tutti i segreti all'avvio da un insieme di credenziali nell'API di configurazione. Questa tecnica consente di accedere a tutti i segreti con il nome, usando la stessa interfaccia IConfiguration usata per il resto della configurazione. Le app che usano AddAzureKeyVault richiedono entrambe le autorizzazioni Get e List per l'insieme di credenziali.

Suggerimento

Indipendentemente dal framework o dal linguaggio usato per compilare l'app, progettarla in modo da memorizzare i valori segreti nella cache locale o caricarli in memoria all'avvio, a meno che non si abbia un motivo valido per non farlo. Leggere i segreti direttamente dall'insieme di credenziali ogni volta che sono necessari è un'attività lenta e costosa.

AddAzureKeyVault richiede solo il nome dell’insieme di credenziali come input, che si ottiene dalla configurazione dell'app locale. Gestisce automaticamente anche l'autenticazione dell'identità gestita. Quando usato in un'app distribuita nel servizio app di Azure in cui sono abilitate le identità gestite per le risorse di Azure, rileva il servizio token delle identità gestite e lo usa per eseguire l'autenticazione. È una scelta ideale per la maggior parte degli scenari e implementa tutte le procedure consigliate. Si usa nell'esercizio di questa unità.

Leggere i segreti in un'app Node.js

L'API Azure Key Vault è un'API REST che gestisce tutte le attività di gestione e utilizzo delle chiavi e degli insiemi di credenziali. Ogni segreto in un insieme di credenziali dispone di un URL univoco. I valori dei segreti vengono recuperati con le richieste HTTP GET.

Il client Key Vault ufficiale per le app Node.js è la classe SecretClient nel pacchetto npm @azure/keyvault-secrets. Le app che includono nomi di segreti nella loro configurazione o nel codice in genere usano il rispettivo metodo getSecret che carica un valore segreto in base al nome. getSecret richiede che l'identità dell'app disponga dell'autorizzazione Get per l'insieme di credenziali. Le app progettate per caricare tutti i segreti da un insieme di credenziali usano anche il metodo listPropertiesOfSecrets, che carica un elenco di segreti e richiede l'autorizzazione List.

Prima di poter creare un'istanza di SecretClient, l'app deve ottenere un oggetto Credenzial per l'autenticazione dell'insieme di credenziali. Per eseguire l'autenticazione, usare la classe DefaultAzureCredential fornita dal pacchetto npm @azure/identity. Il DefaultAzureCredential è appropriato per la maggior parte degli scenari in cui l'applicazione è destinata a essere eseguita nel cloud di Azure perché DefaultAzureCredential combina le credenziali comunemente usate per l'autenticazione al momento della distribuzione con le credenziali usate per l'autenticazione in un ambiente di sviluppo. I tentativi di autenticazione di DefaultAzureCredential vengono eseguiti usando i meccanismi seguenti in ordine:

  • Ambiente. L’DefaultAzureCredential legge le informazioni sull'account specificate tramite le variabili di ambiente e le usa per l'autenticazione.
  • Identità gestita. Se l'applicazione viene distribuita in un host di Azure con identità gestita abilitata, lo DefaultAzureCredential esegue l'autenticazione con tale account.
  • Visual Studio Code. Se ha eseguito l'autenticazione usando il plug-in dell'account Azure di Visual Studio Code, lo DefaultAzureCredential esegue l'autenticazione con tale account.
  • Interfaccia della riga di comando di Azure. Se ha eseguito l'autenticazione di un account tramite il comando az login dell'interfaccia della riga di comando di Azure, lo DefaultAzureCredential esegue l'autenticazione con tale account.

Per altre informazioni, vedere la documentazione di .

Suggerimento

Indipendentemente dal framework o dal linguaggio usato per compilare l'app, progettarla in modo da memorizzare i valori segreti nella cache locale o caricarli in memoria all'avvio, a meno che non si abbia un motivo valido per non farlo. Leggere i segreti direttamente dall'insieme di credenziali ogni volta che sono necessari è un'attività lenta e costosa.

Gestione di segreti in un'app

Dopo aver caricato un segreto nell'app, sarà l'app a gestirlo in modo sicuro. Nell'app che si crea in questo modulo, il valore del segreto viene scritto nella risposta del client e visualizzato in un Web browser per dimostrare che è stato caricato correttamente. La restituzione di un valore del segreto al client non è un'operazione usuale. I segreti in genere sono usati, ad esempio, per inizializzare le librerie client di database o di API remote.

Importante

Esaminare sempre con attenzione il codice per verificare che l'app non scriva i segreti in output di qualunque tipo, inclusi log, archiviazione e risposte.

Esercizio

Per caricare il segreto dall'insieme di credenziali, viene creata una nuova API Web di ASP.NET Core e viene usato il metodo AddAzureKeyVault.

Creare l'app

  1. Eseguire questo comando in Azure Cloud Shell per creare una nuova app API Web di ASP.NET Core e aprirla nell'editor.

    dotnet new webapi -o KeyVaultDemoApp
    cd KeyVaultDemoApp
    code .
    
  2. Dopo il caricamento dell'editor, aggiungere il pacchetto NuGet contenente AddAzureKeyVault e ripristinare tutte le dipendenze dell'app. In Azure Cloud Shell eseguire i comandi riportati di seguito.

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

Aggiungere il codice per caricare e usare i segreti

Per mostrare l'uso ottimale di Key Vault, modificare l'app in modo che carichi i segreti dall'insieme di credenziali all'avvio. Si aggiunge anche un nuovo controller con un endpoint che ottiene il segreto SecretPassword dall'insieme di credenziali.

  1. Per l'avvio dell'app, immettere il comando seguente per avviare l'editor.

    code .
    
  2. Aprire Program.cs, eliminare il contenuto e sostituirlo con il codice seguente.

    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

    Verificare di aver salvato i file dopo aver apportato le modifiche. È possibile salvare i file tramite il menu "..." o i tasti di scelta rapida (CTRL+S in Windows e Linux, Comando++S in macOS).

    L'unica modifica rispetto al codice di avvio è l'aggiunta di ConfigureAppConfiguration. In questo elemento viene caricato il nome dell'insieme di credenziali dalla configurazione e chiamato il metodo AddAzureKeyVault.

  3. Per il controller, creare un nuovo file nella cartella Controllers denominato SecretTestController.cs e incollare nel file il codice seguente.

    Suggerimento

    Per creare un nuovo file, usare il comando touch in Cloud Shell. In questo caso, eseguire il comando touch Controllers/SecretTestController.cs. Per trovarlo, nell'angolo in alto a destra del riquadro File dell'editor selezionare l'icona Aggiorna.

    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. Eseguire il comando dotnet build in Azure Cloud Shell per assicurarsi che tutto venga compilato. L'app è pronta per l'esecuzione. Ora è il momento di inserirla in Azure.

Creare una nuova API Web con Express.js e usare i pacchetti @azure/keyvault-secrets e @azure/identity per caricare il segreto dall'insieme di credenziali.

Creare l'app

Eseguire questo codice in Azure Cloud Shell per inizializzare una nuova app Node.js, installare i pacchetti necessari e aprire un nuovo file nell'editor.

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

Aggiungere il codice per caricare e usare i segreti

Per dimostrare l'uso ottimale di Key Vault, l'app carica i segreti dall'insieme di credenziali all'avvio. Per dimostrare che i segreti sono stati caricati, creare un endpoint che visualizzi il valore del segreto SecretPassword.

  1. Per configurare l'app, incollare il codice seguente nell'editor. Il codice importa i pacchetti necessari, verrà imposta la configurazione dell'URI dell'insieme di credenziali e della porta e crea un nuovo oggetto per contenere i nomi e i valori dei segreti.

    // 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

    Assicurarsi di salvare i file mentre si lavora su di essi, in particolare al termine. È possibile salvare i file tramite il menu "..." o i tasti di scelta rapida (CTRL+S in Windows e Linux, Comando++S in macOS).

  2. Successivamente, si aggiunge il codice per eseguire l'autenticazione all'insieme di credenziali e caricare i segreti. Il codice viene aggiunto sotto forma di due funzioni distinte. Inserire un paio di righe vuote dopo il codice aggiunto in precedenza e quindi incollarvi il codice seguente.

    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. Per testare il caricamento del segreto, creare l'endpoint di Express. Incollare questo codice.

    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. Chaiamare le funzioni per caricare i segreti dall'insieme di credenziali, quindi avviare l'app. Per completare l'app, incollare quest'ultimo frammento di codice.

    (async () =>  {
      await getKeyVaultSecrets();
      app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
      });
    })().catch(err => console.log(err));
    
  5. La scrittura del codice è stata completata, quindi assicurarsi di salvare il file.

L'app è pronta per l'esecuzione. Ora è il momento di inserirla in Azure.