🖥️

ユーザー割り当てマネージドIDを使用してKey Vaultのキーにアクセスする

2024/11/04に公開

目的

ちゃんとKey Vaultを勉強して使えるようにする。

現状

  1. 複数のリソースで同じ接続文字列を使用しており、新しいサーバを立てるたびに環境変数に設定していた
  2. 複数のリソースで同じsecretを使用しており、不定期に入れ替えてた

改善したいこと

  1. 一度設定したインフラはあまり変更したくない
  2. secretの入れ替え時に過去の履歴を残しておきたい(今はExcelで残している)

改善の方向性

secretはKey Vaultに格納し、ユーザー割り当てマネージドIDを使用して各secretにアクセスする。インフラは初期設定以後に変更することはなく、接続文字列やsecretは共通化される。

試行錯誤

サンプルコード

プロジェクト名がアレなのは見逃してください。

サンプルコードを使用するためには下記の設定が必要です。

マネージドIDの種類

上記Learnにある通り、マネージドIDにはシステム割り当てマネージドIDとユーザー割り当てマネージドIDがある。私が理解した限り、使い分けは下記の通り。

  • システム割り当てマネージドID
    • 『システム割り当てサービス プリンシパルの名前は、それが作成された Azure リソースの名前と常に同じです』
    • 例えばサービス(Web/APIなど)とKey Vaultが一つずつしかなく、1:1で紐づける場合に使う
  • ユーザー割り当てマネージドID
    • 『ユーザー割り当て ID は、複数のリソースで使用できます』
    • 例えばサービス(Web/APIなど)が複数とKey Vaultが一つあり、多:1で紐づける場合に使う
    • 例えばマイクロサービスがたくさんあり、そのすべてが同じデータベースに接続し、その接続情報がKey Vaultに格納されている場合に使う

テストプロジェクト

ASP.NET APIプロジェクトで開始する。

Key Vaultをプロジェクトに追加する(Key Vault 新規作成)

依存関係としてKey Vaultを追加する。本操作により、Azure上にKey Vaultが作成される。

コンテナーのURI」を控えておく。

Program.cs 変更

本番環境ではAzure Key Vaultを使用するようコードを追加。

using Azure.Identity;

var builder = WebApplication.CreateBuilder(args);

+ // 環境に応じて設定を切り替える
+ if (!builder.Environment.IsDevelopment())
+ {
+     // 本番環境ではAzure Key Vaultを使用
+     var managedIdentityClientId = Environment.GetEnvironmentVariable("ManagedIdentityClientId");
+     var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("VaultUri")!);
+     var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
+     {
+         ManagedIdentityClientId = managedIdentityClientId
+     });
+ 
+     builder.Configuration.AddAzureKeyVault(keyVaultEndpoint, credential);
+ }

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (true || app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

appsettings.Development.json 変更

ローカル環境用でのキーを appsettings.Development.json に定義する。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
+  "TestData": {
+    "SecretValue": "local-secret-value"
+  }
}

Key Vaultで設定するときはキー名は「TestData--SecretValue」にする。

(root)\Controllers\TestController.cs 新規作成

キーの取得、返却処理を新規に作成。

using Microsoft.AspNetCore.Mvc;

namespace WebApplication10.API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class MyController : ControllerBase
    {
        private readonly IConfiguration _configuration;

        public MyController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpGet]
        public IActionResult GetSecret()
        {
            var secretValue = _configuration["TestData:SecretValue"];
            return Ok(secretValue);
        }
    }
}

発行

通常通り発行します。

が、このままだと起動しないはずです。Program.cs 15行目の new Uri にnullを渡しているため、下記のようなエラーがでるためです。

Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'uriString')
   at System.ArgumentNullException.Throw(String paramName)
   at System.ArgumentNullException.ThrowIfNull(Object argument, String paramName)
   at System.Uri..ctor(String uriString)
   at Program.<Main>$(String[] args) in C:\Users\hydra\source\repos\WebApplication10\WebApplication10.API\Program.cs:line 15
/opt/startup/startup.sh: line 18:    96 Aborted                 (core dumped) dotnet WebApplication10.API.dll

ユーザーマネージドID 新規作成

「マネージドID」を検索し、リソースを作成する。

「作成」する

リソース作成後、「クライアントID」を控えておく。

ユーザーマネージドIDとKey Vaultの接続

事前に作成した「WebApplication10-test」(Key Vault)を選択し、アクセスポリシーの新規作成を行う。

ユーザーマネージドIDとWeb Apps(App Service)の接続

Web AppsのIDからユーザー割り当て済みタブを選択する。

上記で作成したユーザーマネージドIDを追加する。

環境変数の設定

「ManagedIdentityClientId」にユーザーマネージドIDのクライアントID、「VaultUri」にKey VaultのコンテナーのURIをそれぞれ控えておいたものから転記する。

接続確認

サンプルコードではDevelopment/Productionのいずれもswaggerを有効にしてるので、/swagger で接続して、エンドポイントの /Test を確認し、シークレットが表示されることを確認する。

起動できないときのチェック

  • ユーザーマネージドIDとKey Vaultの接続はしたか?
  • ユーザーマネージドIDとWeb Apps(App Service)の接続はしたか?
  • 環境変数の設定はしたか?(環境変数名などは正しいか?)
    • 環境変数設定後、正しく起動したか?
  • Key Vaultでシークレットを設定したか?
    • 上記説明内にはないが、設定が必要
  • Key Vaultでアクセス許可に「取得」「一覧」を追加したか?(今回のコードでは必須)
  • 上記でも解消しない場合はKuduのLog streamでエラー内容を確認したか?

感想

ちゃんと設定しながら進めてたつもりだったんですけど、設定が漏れていたり、コードがちょっとおかしかったり、再起動がうまくいってないだけだったりと(いつも通り)波乱万丈でした。

プロジェクトに組み込める程度にはきれいに実験できたのではないかと思います。
久々に記事にできてよかった。

宣伝

今回は耐久配信を聞きながらひたすら実験してました。
推しに感謝。

VTuberに興味ある方はぜひ。

Discussion