연습 - Azure Key Vault에 저장된 비밀에 액세스

완료됨

Azure 리소스에 대한 관리 ID를 사용하도록 설정하면 앱에서 인증에 사용할 ID가 만들어지는 방법을 알고 있습니다. 이제 해당 ID를 사용하여 자격 증명 모음의 비밀에 액세스하는 앱을 만듭니다.

ASP.NET Core 앱에서 비밀 읽기

Azure Key Vault API는 키 및 자격 증명 모음의 모든 관리와 사용을 처리하는 REST API입니다. 자격 증명 모음의 각 비밀에는 고유한 URL이 있습니다. 비밀 값은 HTTP GET 요청으로 검색됩니다.

.NET Core용 공식 Key Vault 클라이언트는 Azure.Security.KeyVault.Secrets NuGet 패키지의 SecretClient 클래스입니다. 그러나 직접 사용할 필요는 없습니다. ASP.NET Core의 AddAzureKeyVault 메서드를 사용하면 시작 시 자격 증명 모음의 모든 비밀을 구성 API로 로드할 수 있습니다. 이 방법을 사용하면 구성의 나머지 부분에 사용하는 것과 동일한 IConfiguration 인터페이스를 사용하여 이름별로 모든 비밀에 액세스할 수 있습니다. AddAzureKeyVault를 사용하는 앱에는 자격 증명 모음에 대한 GetList 권한이 모두 필요합니다.

앱을 빌드하는 데 사용하는 프레임워크나 언어와 관계없이, 특별한 이유가 없는 한 비밀 값을 로컬로 캐시하거나 비밀을 시작 시 메모리에 로드하도록 설계해야 합니다. 필요할 때마다 자격 증명 모음에서 직접 비밀을 읽으면 불필요하게 속도가 느려지고 비용이 많이 듭니다.

AddAzureKeyVault는 자격 증명 모음 이름만 입력으로 요구하며 이 이름은 로컬 앱 구성에서 가져옵니다. 관리 ID 인증을 자동으로 처리하는 역할도 합니다. Azure 리소스에 대한 관리 ID를 사용하도록 설정된 Azure App Service에 배포된 앱에서 사용될 경우 관리 ID 토큰 서비스를 검색하고 이를 사용하여 인증합니다. 대부분의 시나리오에 적합하며 모든 모범 사례를 구현합니다. 이 단원의 연습에서 사용합니다.

Node.js 앱에서 비밀 읽기

Azure Key Vault API는 키 및 자격 증명 모음의 모든 관리와 사용을 처리하는 REST API입니다. 자격 증명 모음의 각 비밀에는 고유한 URL이 있습니다. 비밀 값은 HTTP GET 요청으로 검색됩니다.

Node.js 앱의 공식 Key Vault 클라이언트는 @azure/keyvault-secrets npm 패키지의 SecretClient 클래스입니다. 구성이나 코드에 비밀 이름을 포함하는 앱은 일반적으로 이름이 지정된 비밀 값을 로드하는 getSecret 메서드를 사용합니다. getSecret에서는 앱의 ID에 자격 증명 모음에 대한 Get 권한이 필요합니다. 자격 증명 모음에서 모든 비밀을 로드하도록 고안된 앱은 listPropertiesOfSecrets 메서드도 사용하며, 이 메서드는 비밀 목록을 로드하고 List 권한이 필요합니다.

앱에서 SecretClient 인스턴스를 만들려면 먼저 자격 증명 모음에 인증하기 위해 자격 증명 개체를 가져와야 합니다. 인증하려면 @azure/identity npm 패키지에서 제공하는 DefaultAzureCredential을 사용합니다. DefaultAzureCredential은 애플리케이션이 궁극적으로 Azure Cloud에서 실행되도록 의도된 대부분의 시나리오에 적절합니다. DefaultAzureCredential은 배포 시 인증에 일반적으로 사용되는 자격 증명과 개발 환경에서 인증하는 데 사용되는 자격 증명을 결합하기 때문입니다. DefaultAzureCredential은 다음 메커니즘을 순서대로 사용하여 인증을 시도합니다.

  • 환경. DefaultAzureCredential은 환경 변수를 사용하여 지정된 계정 정보를 읽고 이를 사용하여 인증합니다.
  • 관리 ID. 관리 ID가 사용하도록 설정된 Azure 호스트에 애플리케이션이 배포되면 DefaultAzureCredential은 해당 계정으로 인증됩니다.
  • Visual Studio Code. 개발자가 Visual Studio Code Azure 계정 플러그 인을 사용하여 인증한 경우 DefaultAzureCredential은 해당 계정으로 인증합니다.
  • Azure CLI 개발자가 Azure CLI az login 명령을 사용하여 계정을 인증한 경우 DefaultAzureCredential은 해당 계정으로 인증합니다.

자세한 정보는 설명서를 참조하세요.

앱을 빌드하는 데 사용하는 프레임워크나 언어와 관계없이, 특별한 이유가 없는 한 비밀 값을 로컬로 캐시하거나 비밀을 시작 시 메모리에 로드하도록 설계해야 합니다. 필요할 때마다 자격 증명 모음에서 직접 비밀을 읽으면 불필요하게 속도가 느려지고 비용이 많이 듭니다.

앱에서 비밀 처리

비밀이 앱에 로드된 후 비밀을 안전하게 처리하는 것은 앱의 책임입니다. 이 모듈에서 빌드한 앱의 경우 클라이언트 응답에 비밀 값을 기록하고, 비밀 값이 성공적으로 로드되었음을 보여 주기 위해 웹 브라우저에서 비밀 값을 살펴보겠습니다. 클라이언트에 비밀 값을 반환하는 것은 일반적으로 수행하는 작업이 ‘아닙니다’. 일반적으로 데이터베이스 또는 원격 API의 클라이언트 라이브러리 초기화와 같은 작업을 수행하는 데 비밀을 사용합니다.

Important

앱이 로그, 스토리지 및 응답을 비롯한 모든 종류의 출력에 비밀을 쓰지 않도록 항상 코드를 주의 깊게 검토하세요.

연습

자격 증명 모음에서 비밀을 로드하기 위해 새 ASP.NET Core 웹 API를 만들고 AddAzureKeyVault를 사용하겠습니다.

앱 만들기

  1. 새 ASP.NET Core 웹 API 앱을 만들고 편집기에서 열려면 Azure Cloud Shell에서 다음 명령을 실행합니다.

    dotnet new webapi -o KeyVaultDemoApp
    cd KeyVaultDemoApp
    code .
    
  2. 편집기가 로드된 후 AddAzureKeyVault를 포함하는 NuGet 패키지를 추가하고 모든 앱의 종속성을 복원합니다. Azure Cloud Shell에서 다음 명령을 실행합니다.

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

비밀을 로드하고 사용하는 코드를 추가합니다.

Key Vault를 효과적으로 사용하려면 시작 시 자격 증명 모음에서 비밀을 로드하도록 앱을 수정합니다. 또한 자격 증명 모음에서 SecretPassword 비밀을 가져오는 엔드포인트가 있는 새 컨트롤러를 추가합니다.

  1. 앱 시작 시 다음 명령을 입력하여 편집기를 시작합니다.

    code .
    
  2. Program.cs를 열고, 콘텐츠를 삭제하고, 다음 코드로 바꿉니다.

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

    중요

    편집이 완료되면 파일을 저장해야 합니다. "..." 메뉴 또는 액셀러레이터 키(Windows 및 Linux에서는 Ctrl+S, macOS에서는 Cmd+S)를 사용하여 파일을 저장할 수 있습니다.

    시작 코드의 유일한 변경 내용은 ConfigureAppConfiguration를 추가하는 것입니다. 이 요소를 통해 구성에서 자격 증명 모음 이름을 로드하고 AddAzureKeyVault를 함께 호출합니다.

  3. 컨트롤러의 경우 SecretTestController.cs라고 하는 Controllers 폴더에 새 파일을 생성하고 다음 코드에 붙여넣습니다.

    새 파일을 만들려면 Cloud Shell에서 touch 명령을 사용합니다. 이 경우 touch Controllers/SecretTestController.cs 명령을 실행합니다. 파일을 확인하려면 편집기의 파일 창 오른쪽 위 모서리에서 새로 고침 아이콘을 선택합니다.

    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. Azure Cloud Shell에서 dotnet build 명령을 실행하여 모든 항목이 컴파일되는지 확인합니다. 앱을 실행할 준비가 되었습니다. 이제 Azure로 가져올 시간입니다!

Express.js를 사용하여 새 웹 API를 만들고 @azure/keyvault-secrets@azure/identity 패키지를 사용하여 자격 증명 모음에서 비밀을 로드합니다.

앱 만들기

Azure Cloud Shell에서 다음 코드를 실행하여 새 Node.js 앱을 초기화하고, 필요한 패키지를 설치하고, 편집기에서 새 파일을 엽니다.

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

비밀을 로드하고 사용하는 코드를 추가합니다.

Key Vault의 올바른 사용을 보여 주기 위해 앱은 시작 시 자격 증명 모음에서 비밀을 로드합니다. 비밀이 로드되었음을 입증하려면 SecretPassword 비밀의 값을 표시하는 엔드포인트를 만듭니다.

  1. 앱을 설정하려면 편집기에 다음 코드를 붙여넣습니다. 이 코드는 필요한 패키지를 가져오고, 포트 및 자격 증명 모음 URI 구성을 설정하고, 비밀 이름과 값을 보관할 새 개체를 만듭니다.

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

    이 과정을 진행할 때, 특히 완료하면 파일을 저장해야 합니다. "..." 메뉴 또는 액셀러레이터 키(Windows 및 Linux에서는 Ctrl+S, macOS에서는 Cmd+S)를 사용하여 파일을 저장할 수 있습니다.

  2. 다음으로 자격 증명 모음에 인증하는 코드를 추가하고 비밀을 로드합니다. 이 코드를 두 개의 별도 함수로 추가합니다. 이전에 추가한 코드 뒤에 빈 줄을 몇 개 삽입하고 다음 코드를 붙여넣습니다.

    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. 비밀이 로드되었는지 여부를 테스트하려면 Express 엔드포인트를 만듭니다. 다음 코드를 붙여넣습니다.

    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. 함수를 호출하여 자격 증명 모음에서 비밀을 로드한 다음 앱을 시작합니다. 앱을 완료하려면 이 마지막 코드 조각을 붙여넣습니다.

    (async () =>  {
      await getKeyVaultSecrets();
      app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
      });
    })().catch(err => console.log(err));
    
  5. 코드 작성을 완료했으므로 파일을 저장해야 합니다.

앱을 실행할 준비가 되었습니다. 이제 Azure로 가져올 시간입니다!