Übung: Zugreifen auf in Azure Key Vault gespeicherte Geheimnisse

Abgeschlossen

Sie wissen, wie Sie durch die Aktivierung von verwalteten Identitäten für Azure-Ressourcen eine Identität für Ihre Anwendung erstellen, die Sie zur Authentifizierung verwenden können. Erstellen Sie nun eine Anwendung, die diese Identität verwendet, um auf Geheimnisse im Tresor zuzugreifen.

Lesen von Geheimnissen in einer ASP.NET Core-App

Die Azure Key Vault-API ist eine REST-API, die die gesamte Verwaltung und Nutzung von Schlüsseln und Tresoren verarbeitet. Jedes Geheimnis in einem Tresor hat eine eindeutige URL. Geheime Werte werden mit HTTP GET-Anforderungen abgerufen.

Der offizielle Key Vault-Client für .NET Core ist die Klasse SecretClient im NuGet-Paket Azure.Security.KeyVault.Secrets. Sie müssen sie jedoch nicht direkt verwenden. Mit der AddAzureKeyVault-Methode von ASP.NET Core können Sie beim Start alle Geheimnisse in einem Tresor in die Konfigurations-API laden. So erhalten Sie Zugriff auf alle Ihre Geheimnisse anhand des Namens über dieselbe IConfiguration-Schnittstelle, die Sie für den Rest Ihrer Konfiguration verwenden. Apps, die AddAzureKeyVault verwenden, benötigen für den Tresor die die beiden Berechtigungen Get und List.

Tipp

Unabhängig davon, mit welchem Framework und welcher Sprache Sie Ihre App erstellen, sollten Sie sie dafür konzipieren, die Werte von Geheimnissen zu speichern oder diese beim App-Start in den Speicher zu laden, sofern kein spezifischer Grund dagegen spricht. Es ist unnötig langsam und teuer, Geheimnisse bei Bedarf direkt aus dem Tresor zu lesen.

AddAzureKeyVault benötigt nur den Tresornamen als Eingabe, den Sie aus Ihrer lokalen App-Konfiguration abrufen können. Außerdem erfolgt die Verarbeitung der Authentifizierung verwalteter Identitäten automatisch. Beim Einsatz in einer App, die für Azure App Service mit aktivierten verwalteten Identitäten bereitgestellt ist, wird der Tokendienst für verwaltete Identitäten erkannt und zur Authentifizierung verwendet. Dieser Ablauf eignet sich für die meisten Szenarios und umfasst alle bewährten Methoden. Sie verwenden ihn in der Übung dieser Einheit.

Lesen von Geheimnissen in einer Node.js-App

Die Azure Key Vault-API ist eine REST-API, die die gesamte Verwaltung und Nutzung von Schlüsseln und Tresoren verarbeitet. Jedes Geheimnis in einem Tresor hat eine eindeutige URL. Geheime Werte werden mit HTTP GET-Anforderungen abgerufen.

Der offizielle Key Vault-Client für Node.js-Apps ist die SecretClient-Klasse im npm-Paket @azure/keyvault-secrets. Apps, die Geheimnisnamen in deren Konfiguration oder Code enthalten, verwenden in der Regel die getSecret-Methode, die einen Geheimniswert anhand des Namens lädt. getSecret erfordert, dass die Identität Ihrer App die Get-Berechtigung für den Tresor hat. Apps, die darauf ausgelegt sind, alle Geheimnisse aus einem Tresor zu laden, verwenden ebenfalls die listPropertiesOfSecrets-Methode, die eine Liste von Geheimnissen lädt und die List-Berechtigung erfordert.

Bevor Ihre App eine SecretClient-Instanz erstellen kann, muss sie für die Authentifizierung beim Tresor ein Objekt mit Anmeldeinformationen abrufen. Verwenden Sie zum Authentifizieren das vom npm-Paket @azure/identity bereitgestellte Element DefaultAzureCredential. DefaultAzureCredential ist für die meisten Szenarien geeignet, in denen die Anwendung letztendlich in der Azure Cloud ausgeführt werden soll, da DefaultAzureCredential die Anmeldedaten, die üblicherweise zur Authentifizierung bei der Bereitstellung verwendet werden, mit den Anmeldedaten kombiniert, die zur Authentifizierung in einer Entwicklungsumgebung verwendet werden. DefaultAzureCredential versucht, sich in der angegebenen Reihenfolge über die folgenden Mechanismen zu authentifizieren:

  • Umgebung DefaultAzureCredential liest Kontoinformationen, die mit Umgebungsvariablen angegeben werden, und nutzt diese zur Authentifizierung.
  • Verwaltete Identität Wenn die Anwendung auf einem Azure-Host bereitgestellt wird, während die Funktion „Verwaltete Identität“ aktiviert ist, authentifiziert sich DefaultAzureCredential mit diesem Konto.
  • Visual Studio Code. Wenn sich Entwickler*innen über das Visual Studio Code Plug-In „Azure Account“ authentifiziert haben, authentifiziert sich DefaultAzureCredential mit diesem Konto.
  • Azure-Befehlszeilenschnittstelle. Wenn Entwickler*innen ein Konto über den Befehl „Azure CLI az login“ authentifiziert haben, authentifiziert sich DefaultAzureCredential mit diesem Konto.

Weitere Informationen finden Sie in der Dokumentation.

Tipp

Unabhängig davon, mit welchem Framework und welcher Sprache Sie Ihre App erstellen, sollten Sie sie dafür konzipieren, die Werte von Geheimnissen zu speichern oder diese beim App-Start in den Speicher zu laden, sofern kein spezifischer Grund dagegen spricht. Es ist unnötig langsam und teuer, Geheimnisse bei Bedarf direkt aus dem Tresor zu lesen.

Verarbeiten von Geheimnissen in einer App

Wenn ein Geheimnis in die App geladen wurde, muss diese es sicher verarbeiten. In der App, die Sie in diesem Modul erstellen, schreiben Sie Ihren geheimen Wert in die Client-Antwort und um zu zeigen, dass er erfolgreich geladen wurde, zeigen Sie ihn in einem Webbrowser an. Das Zurückgeben des Geheimniswerts an den Client ist nicht die übliche Vorgehensweise. In der Regel werden Geheimnisse beispielsweise zum Initialisieren von Clientbibliotheken für Datenbanken oder Remote-APIs verwendet.

Wichtig

Überprüfen Sie Ihren Code immer sorgfältig, um sicherzustellen, dass Ihre App nie Geheimnisse in Ausgaben (z. B. Protokolle, Speicherung und Antworten) schreibt.

Übung

Um das Geheimnis aus unserem Tresor zu laden, erstellen Sie eine neue ASP.NET Core Web API und verwenden AddAzureKeyVault.

Erstellen der App

  1. Führen Sie in Azure Cloud Shell die folgenden Befehle aus, um eine neue ASP.NET Core-Web-API-App zu erstellen und im Editor zu öffnen.

    dotnet new webapi -o KeyVaultDemoApp
    cd KeyVaultDemoApp
    code .
    
  2. Fügen Sie nach dem Laden des Editors das NuGet-Paket mit AddAzureKeyVault hinzu, und stellen Sie alle Abhängigkeiten der App wieder her. Führen Sie in Azure Cloud Shell die folgenden Befehle aus.

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

Hinzufügen von Code zum Laden und Verwenden von Geheimnissen

Zur Veranschaulichung der richtigen Verwendung von Key Vault modifizieren Sie Ihre App, sodass Geheimnisse beim Start aus dem Tresor geladen werden. Sie fügen außerdem einen neuen Controller mit einem Endpunkt hinzu, der das Geheimnis SecretPassword aus dem Tresor abruft.

  1. Geben Sie für den Start der App den folgenden Befehl ein, um den Editor zu starten.

    code .
    
  2. Öffnen Sie Program.cs, löschen Sie den Inhalt, und ersetzen Sie diesen durch den folgenden Code.

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

    Wichtig

    Speichern Sie Dateien unbedingt, wenn Sie diese fertig bearbeitet haben. Sie können Dateien entweder über das Menü „…“ speichern oder mit der entsprechenden Tastenkombination (STRG+S unter Windows und Linux bzw. CMD+S unter macOS).

    Die einzige Änderung gegenüber dem Startercode ist das Hinzufügen von ConfigureAppConfiguration. Hier laden Sie den Tresornamen aus der Konfiguration und rufen damit AddAzureKeyVault auf.

  3. Erstellen Sie für den Controller im Ordner Controllers eine neue Datei namens SecretTestController.cs, und fügen Sie den folgenden Code ein.

    Tipp

    Verwenden Sie den Befehl touch in Cloud Shell, um eine neue Datei zu erstellen. Führen Sie in diesem Fall den Befehl touch Controllers/SecretTestController.cs aus. Klicken Sie rechts oben im Bereich Dateien des Editors auf das Symbol „Aktualisieren“, damit es dort angezeigt wird.

    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. Führen Sie den dotnet build-Befehl in Azure Cloud Shell aus, um sicherzustellen, dass alles kompiliert wird. Die App ist für die Ausführung bereit. Jetzt ist es an der Zeit, sie in Azure zu integrieren!

Erstellen Sie eine neue Web-API mit Express.js, und verwenden Sie die Pakete @azure/keyvault-secrets und @azure/identity zum Laden des Geheimnisses aus dem Tresor.

Erstellen der App

Führen Sie in Azure Cloud Shell folgenden Code aus, um eine neue Node.js-App zu initialisieren, die benötigten Pakete zu installieren und eine neue Datei im Editor zu öffnen.

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

Hinzufügen von Code zum Laden und Verwenden von Geheimnissen

Zur Veranschaulichung der richtigen Verwendung von Key Vault lädt Ihre App beim Starten Geheimnisse aus dem Tresor. Erstellen Sie einen Endpunkt, der den Wert des Geheimnisses SecretPassword anzeigt, um zu demonstrieren, dass die Geheimnisse geladen wurden.

  1. Fügen Sie den folgenden Code in den Editor ein, um die App einzurichten. Dieser Code importiert die erforderlichen Pakete. Außerdem werden der Port und die URI-Konfiguration des Tresors eingerichtet, und ein neues Objekt für die Geheimnisnamen und -werte erstellt.

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

    Wichtig

    Speichern Sie Dateien, während Sie an diesen arbeiten und insbesondere, wenn Sie fertig sind. Sie können Dateien entweder über das Menü „…“ speichern oder mit der entsprechenden Tastenkombination (STRG+S unter Windows und Linux bzw. CMD+S unter macOS).

  2. Fügen Sie als Nächstes den Code hinzu, um sich beim Tresor zu authentifizieren und die Geheimnisse zu laden. Dieser Code wird in Form von zwei separaten Funktionen hinzugefügt. Fügen Sie ein paar leere Zeilen nach dem Code hinzu, den Sie zuvor hinzugefügt haben, und fügen Sie dann den folgenden Code ein:

    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. Erstellen Sie nun den Express-Endpunkt, um zu testen, ob das Geheimnis geladen wurde. Fügen Sie den folgenden Code ein:

    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. Rufen Sie Ihre Funktionen zum Laden der Geheimnisse aus dem Tresor auf, und starten Sie dann die App. Fügen Sie diesen letzten Codeausschnitt ein, um die App fertigzustellen:

    (async () =>  {
      await getKeyVaultSecrets();
      app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
      });
    })().catch(err => console.log(err));
    
  5. Sie sind jetzt mit dem Programmieren fertig, speichern Sie die Datei also unbedingt.

Die App ist für die Ausführung bereit. Jetzt ist es an der Zeit, sie in Azure zu integrieren!