ASP.NET Core で Azure の Web Apps といった App Service にホストするアプリを作る際の接続族文字列の管理について整理しておきましょう。今回は SQL Database の接続文字列というソースコード管理上におきたくない情報を管理することを例にして進めます。
このブログ書きかけのまま1年以上すぎたネタですが(Key Vault の利用がそれくらい簡単だっていうことでw)、ASP.NET Core はV3系をベースに進めます。
- 余談: ASP.NET Core の環境変数の読み込み
- ローカルデバッグ用に接続文字列の管理: User Secrets
- Web Apps のアプリケーション設定で管理
- Key Vault でシークレットを管理
- Key Vault の使いどころ
- その他: App Configuration / Managed Identity
- まとめ
余談: ASP.NET Core の環境変数の読み込み
いきなり余談ですが、
ASP.NET Core では(.NET Core 3系に GenericHost がデフォになったので、"GenericHost では" といった方が妥当でしょうか)、環境変数はアプリ起動時に Microsoft.Extensions.Configuration namespace の IConfiguration
インターフェースのオブジェクトに格納されます。
こいつへの環境変数の登録処理は、ASP.NET Core 1系のころは何を読み込んでいるかコードが露出していましたが、2系以降 は CreateDefaultBuilder
というメソッドでラップされたので、ぱっとは見えなくなりました。
このメソッドではざっくりと以下のことをしてます。
- 環境変数の読み込み
appsettings.json
( とappsetting.{Environment}.json
)の読み込み- シークレットマネージャー(ユーザーシークレット)の読み込み
他に色々もしてます。詳しくはこちらに記載があります。これで足りない部分は自分で追加するって感じです。
2系でいきなり出てきたころは自分で調べたなぁ...
ASP.NET Core 起動時 の CreateDefaultBuilder() がやってくれること( ASP.NET Core 2.0 ~) - BEACHSIDE BLOG
余談はこの辺にして、どう設定するかの話に進みます。
ローカルデバッグ用に接続文字列の管理: User Secrets
appsettings.json に書くってことは気が狂ってもやらないですね。
appsettings.{Environment}.json を .gitignore にしてってのは有りかもしれませんがそれも微妙です。ソース管理に含めないシークレットは、ユーザーシークレット(User Secrets)に格納します。
サンプルコードで appsettings.json に書いてる例をよく見ますが、その例を見た人が「今回はあえてここに書いてるけどソースコード管理として晒されるのでホントは書いちゃ行けない、別の場所に書くんだよね」とか理解してる人どれだけいますかね...少ないと思うので、今回のような基本的な内容だからこそ appsettings.json にシークレットを入れましょーみたいなリスキーなノリはあかんと思っています。
ユーザーシークレットへの設定は、VS2019で操作するなら、
ASP.NET Core のプロジェクトを右クリック > ユーザーシークレットの管理 を選択すると secret.json というファイルが開きます。ここに appsettings.json 同様に設定してあげればアプリ起動時に読み込んでくれます。
secret.json は、ソースコードとは無関係のパスに保存されています( 例えばこんな感じ:
C:\Users\{username}\AppData\Roaming\Microsoft\UserSecrets\24e6dfb3-016b-44c3-a468-1868dc1ff99c\secrets.json
)。プロジェクトごとに guid がふられてるので、xUnit とかでローカルでテストプログラムを実行する際に使いたければ、これを読み込むよう指定してあげることで使うことができます(セキュリティ関連のちょっと読み込む工夫は必要ですね)。
また話が脱線しますが、IConfiguration
に格納された値は、拡張メソッド の GetValue
とか GetSection
とか GetConnectionString
で取得します(最も使うオプションパターンについてはここでは触れません)。
GetConnectionString
は、内部的に return configuration?.GetSection("ConnectionStrings")?[name];
って実装なので、結局 json をどう書くか次第で読み込み方法が変わる程度です。GetConnectionString
を使って読み込むならjsonはこんな感じで書きます。
{ "ConnectionStrings": { "my-sqldb": "接続文字列を書く..." } }
GetConnectionString
を使うなら
var val = Configuration.GetConnectionString("my-sqldb");
他の Get**
メソッドで値を取得するなら、
var val= Configuration.GetSection("ConnectionStrings")?["my-sqldb"];
とか
var val= Configuration.GetValue<string>("ConnectionStrings:my-sqldb");
って感じです。結局内部的に同じことしてるのでそれさえわかってれば、どちらで実装してもよいと思っています。
Web Apps のアプリケーション設定で管理
次は、Azure の Web Apps 上での接続文字列の管理の話です。ここでの方法は、伝統的でまだまだ多く使われているとは思いますが、お手軽 = 一番しょぼいやり方ともいえます(状況次第なのでいいとか悪いとかって話ではないですw)。
アプリケーション設定に直接設定する
Azure ポータルで Web Apps を開き 構成 を開きましょう。アプリケーション設定のタブに アプリケーション設定 と 接続文字列 があります。
これのどちらかに設定すればよいです。どっちでもいいんですが、前述で書いた ConngectionString なのかそうでないのかって差がでます(つまり書き方に応じた読み込み方に違いがあるだけです)。
ASP.NET Core の startup.cs の ConfigureServices メソッド内で、 GetConnectionString メソッドを使って以下のように書いてるとしましょう。
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<SampleContext>(options => options.UseSqlServer(Configuration.GetConnectionString("my-sqldb"))); // 以下略
そうすると Web Apps のアプリケーション設定では、以下図のように接続文字列 にキーと値のを設定してあげれば動きます。
どうでもよいですが、接続文字列ではなく アプリケーション設定の方に ConnectionStrings:my-sqldb
ってキーと値を設定すれば、コードを変更せずに正常に動作します。
ここに設定しておくと、WebApps が見れる開発者が接続文字列を見れることになるため、イマイチなこともあるかもしれません。
Key Vault でシークレットを管理
次は、Azure の Key Vault というキーやシークレットや証明書を管理する専用のサービスを使った方法です。接続文字列を Key Vault のシークレットに設定し、 Web Apps からは Key Vault を参照するようにします。そうすると開発者は Key Vault の中を見る権限がないと接続文字列が分かりません。
では以下の流れで設定してみましょう。
- Key Vault のシークレットに登録
- Web Apps から Key Vault にアクセスできるよう設定
- Web Apps のアプリケーション設定で Key Vault のキーを設定
Key Vault のシークレットに登録
設定方法は、まず Azure で Key Vault のインスタンスを作ります。作り方は簡単なので省略しますが こちら を見てさくっと作れます。
リソースを作成したら Key Vault のリソース開く > シークレット を開き、キーと接続文字列の値を設定しましょう。
Web Apps から Key Vault にアクセスできるよう設定
Web Apps で Managed Id を設定する
Azure ポータルで Web Apps のリソースを開き、ID をクリックします。
システム割り当て済み タブで 状態を オン にしましょう。オブジェクトIDが生成されますので、これをコピーしておきます。
Web Apps からアクセスできるよう Key Vault の設定をする
Key Vault 側でアクセスポリシーを設定することで、特定の Web Apps からアクセスを許可するよう設定します。
Azure ポータルで Key Vault のリソースを開く > アクセスポリシーをクリック > アクセスポリシーの追加 をクリックします。以下のように最低限の設定だけします。
- シークレットのアクセス許可: 取得 を選択
- プリンシパルの選択: 先ほど WebApps 側で取得したオブジェクトIDを入力すると対象のリソースが検索できますので追加します。
Web Apps のアプリケーション設定で Key Vault のキーを設定
まず Key Vault のリソースのメニューで シークレット > 先ほど作成したキーをクリック > 利用するバージョンをクリック(一度しか登録してなければ一つしか表示されません)します。
シークレット識別子をコピーします。
で、実際に WebApps に入力する値は、例えばシークレット識別子が https://hoge.vault.azure.net/secrets/my-secret/7bf1c27bda8a4f089994746932496021f
だったら
@Microsoft.KeyVault(SecretUri=https://hoge.vault.azure.net/secrets/my-secret/7bf1c27bda8a4f089994746932496021f)
となります。この詳しい構文は、こちらに記載があります。
この値を、Web Apps の アプリケーション設定 で、値の方に設定するだけです。
ちなみに アプリケーション設定 の中の 接続文字列 のほうで設定せず、アプリケーション設定 で設定した場合は、以下図のように Key valut Reference
のチェックマークが表示されてかわいいです。接続できないよう設定したら×マークがつきました。
Key Vault に設定することで Web Apps のリソースを閲覧できる人に接続文字列をさらさず設定できます。
Key Vault の使いどころ
シンプルに構成するなら Web Apps のアプリケーション設定でいいですが、Azure ポータルで Web Apps のリソースから見られたくない場合は Key Vault を使うってのが判断のひとつでしょうかね。
Web Apps のリソースのロールが read ならアプリケーション設定の値は見ることができないですが、それ以上の権限を与えたいけどシークレットは見せたくないときとか。
あとは、Web Apps にない機能で Key Vault にしかないバージョン管理機能とかが必要な場合なども考えられます。
それなら key vault 使わず IaC で管理するって手段もあるので、必要性はその状況?背景?コンテキスト?によります。
その他: App Configuration / Managed Identity
App Configuration
環境変数の取得方法のコードにほんのちょっと手を加えて別の手段で管理するものとして、App Configuration があります。
これは、Web Apps のアプリケーション設定だけを切り出したサービスのようなものです(雑)。マイクロサービスを構築する際、特定のドメインの中に Functions がたーくさんあって同じ環境変数を使うような状況だといい感じに使えます。
プレビューが公開されたばかりの頃にしばやん先生が一通り試してましたね。
Managed Identity
Key Vault を使ったときにも使いましたが、このアクセス制御をちょっと変えたようなものです。今のところ使うタイミングがなさそうで興味が少ないのでリンクのみにしておきます。
まとめ
無駄に複雑なことせずケースバイケースで一番シンプルな構成にするのがよいっすね。