しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Functions の Binding / Trigger で必要なシークレットをより安全に扱う

Azure Functions は非常に便利ですが、Azure Storage や Cosmos DB などの Binding / Trigger を使う際に必要となる接続文字列は、管理を考えると若干扱いが面倒な部分があります。

App Settings に直接値を設定するのが簡単ですが、権限管理という観点では値が簡単に確認出来てしまうのと、複数のサービスを利用する場合にはそれぞれの接続文字列を設定する必要があるので、再生成や更新といった際の管理コストが高くなりがちです。

最近ではこの辺りの課題を解決するための手段が増えてきているので、実際に動作を試しておきました。

Key Vault Reference を使って参照する

接続文字列などを安全に管理する際には Key Vault Reference を使うのが最近の定番になっていると思います。App Settings に設定はしますが実体は Key Vault に格納されているのと、Key Vault 自体へのアクセスは Managed Identity で行うので最小限の権限で利用できます。

そんな Key Vault Reference にも弱点があって、Key Vault へのアクセスを Service Endpoint / Private Endpoint のみに限定している場合には、Azure Functions が Regional VNET Integration で VNET に参加していても利用できませんでした。つまりパブリックアクセスが必要でした。

最近になってその制約が解消されたので、App Service や Key Vault などを全て VNET に入れつつ、Key Vault Reference で安全に機密情報を扱えるようになりました。

前回の Hack Azure! #5 の時にも少し紹介しましたが、正式に GA したと発表があったので安心して使えます。

動作確認は Private Endpoint 経由のみ許可した Key Vault を用意して行いました。Private Endpoint を有効化しつつ、以下のように Firewall で全てを拒否するように設定することで実現出来ます。

f:id:shiba-yan:20210408030307p:plain

事前に Azure Functions では Managed Identity を有効にして、Key Vault の RBAC 設定で Secret への読み込み権限を与えておきます。最近は Access Policy より RBAC を使うようにしています。

これで Key Vault Reference を設定すると、Managed Identity を使ってシークレットを取得できているのが確認できます。Private Endpoint のみ許可しているので、VNET 経由で取りに行っていることも確認出来ます。

f:id:shiba-yan:20210408031115p:plain

注意点としては現時点はバージョン無しの Secret を参照している場合には、その更新を検知して再起動する機能は正しく動作しないようです。対応予定のようですが、今はバージョンまで指定する必要があります。

Function App から接続文字列を上書きする

Key Vault Reference が VNET 経由に対応したので使う場面が減った感はありますが、Azure Functions Runtime 3.0.15417 から Runtime Scale Monitoring を有効にしている環境で、Function App から Binding / Trigger が参照するシークレットを更新できるようになりました。

具体的には DI を使って Configuration Source をカスタマイズする形になります。

メリットとしては Configuration Source は Function App 側で実行されるので、Key Vault Reference のように VNET 周りの制約を受けることなく使えるという点があります。App Settings に 1 つずつ設定する必要が無いというのもメリットです。

Key Vault に保存されたシークレットを使うのは、最初からパッケージが用意されているので簡単です。

具体的に Azure Functions の Configuration Source に Key Vault を追加するのは以下のようなコードだけで行えます。当然ながら Managed Identity を使って Key Vault へアクセスしています。

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
    }

    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        var builtConfig = builder.ConfigurationBuilder.Build();

        var secretClient = new SecretClient(new Uri(builtConfig["KeyVaultEndpoint"]), new DefaultAzureCredential());

        builder.ConfigurationBuilder.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());
    }
}

Runtime Scale Monitoring の有効化を行わずにデプロイすると、Azure Portal に以下のようなエラーが表示されます。Scale Controller が参照する値とアプリケーションが参照する値が異なると困るので、基本は Binding / Trigger が参照するキーは変更不可になっているからです。

f:id:shiba-yan:20210408024608p:plain

その後 Runtime Scale Monitoring を有効化すると、問題なく動作するようになります。

f:id:shiba-yan:20210408025840p:plain

Cosmos DB や Storage を丸ごと Private Endpoint で閉じてしまうようなケースでは、Premium Plan を使っていても Scale Controller は利用できなくなるので Runtime Scale Monitoring が必要になります。

その場合は Key Vault Reference よりも Configuration Source を使った方が楽かもしれません。

Managed Identity を使って接続文字列を不要にする

現在プレビューで公開されている拡張を使うと、Storage などへのアクセスを Managed Identity で行えるようになるので、そもそも Key Vault で接続文字列を管理する必要が無くなります。これによって理想的な形で権限管理が行えるようになります。

ドキュメントには Blob / Queue / Event Hubs が Managed Identity に対応しているとありますが、恐らく Service Bus もプレビュー版が出ているので使える可能性が高いです。

基本的な使い方はプレビュー版の拡張をインストールしてしまえば、あとは接続文字列を Managed Identity 向けに変更するだけなので簡単です。プレビュー版の拡張は参照している Azure SDK が新しくなっているので、クラス名などに多少の破壊的変更があることだけ注意が必要です。

Managed Identity は接続文字列が不要ですが、接続するリソースに関する情報は必要なので serviceUrifullyQualifiedNamespace だけを設定するようにします。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "StorageConnectionString__serviceUri": "https://***.queue.core.windows.net/",
    "EventHubConnectionString__fullyQualifiedNamespace": "***.servicebus.windows.net"
  }
}

内部的には接続文字列は階層構造を持っているので、上のような定義が出来るようになっています。キー名に関しては拡張によって変わるのでドキュメントを参照する必要があります。

App Settings でも同様に設定を追加しておきます。接続先のリソース名は Key Vault で守るような情報ではないのでそのまま書いてしまいます。

f:id:shiba-yan:20210408021038p:plain

Managed Identity を使うので Storage と Event Hubs に対して必要な権限を RBAC で追加すれば完了です。

適当に Queue や Event Hubs に対してメッセージを追加すると、接続文字列無しで動作していることが確認できます。若干地味な機能ですが、1 つの System Assigned Managed Identity で複数のリソースへのアクセスが出来るのは便利です。

f:id:shiba-yan:20210408020929p:plain

f:id:shiba-yan:20210408020524p:plain

順調に Binding / Trigger 周りで Managed Identity への対応が進んでいるので、将来的には Cosmos DB などでも Managed Identity でシンプルな設定と権限管理が実現されるはずです。

対応が完了すると Azure Functions は Regional VNET Integration と Managed Identity によってセキュアな PaaS / Serverless として大きな進化を遂げそうです。