デプロイスロットをスワップするとすべてのユーザーがログアウトされてしまう問題
Azure App ServiceでホストされているASP.NET Coreアプリケーションでデプロイスロットを切り替えると、ログインしていたすべてのユーザーがログアウトされてしまうという問題に遭遇しました。
調べてみると、Microsoftのドキュメントに下記のような記載がありました。
アプリが Azure Apps でホストされている場合、キーは %HOME%ASP.NET_DataProtection-Keys フォルダーに保持されます。 このフォルダーはネットワーク ストレージにバックアップされ、アプリをホストしているすべてのマシンで同期されています。
- 保存中のキーは保護されていません。
- DataProtection-Keys フォルダーから、単一のデプロイ スロットのアプリのすべてのインスタンスにキー リングが提供されます。
- ステージングや運用などの別のデプロイ スロットでは、キー リングが共有されません。 ステージングから運用へのスワップや A/B テストの使用など、デプロイ スロットをスワップすると、データ保護を使っているアプリからは、前のスロット内のキー リングを使って格納されたデータを復号化できなくなります。 その結果、標準の ASP.NET Core cookie 認証を使うアプリでは、データ保護を使って cookie が保護されているため、ユーザーがログアウトされます。 スロットに依存しないキー リングが必要な場合は、Azure Blob Storage、Azure Key Vault、SQL ストア、Redis キャッシュなどの外部キー リング プロバイダーを使います。
データ保護キーは、認証cookieの保護やCSRFトークン生成時のキーとして利用されるものですが、Asp.NETCoreアプリの既定の設定ではデプロイスロット間で共有されない場所に保管されるためにスロットの切り替え後に認証cookieが復号できずにログアウトされてしまうようです。
Azure Blob Storage と Azure Key Vaultを利用したデータ保護キーの構成
上記の問題は、異なるデプロイスロット間で共有できる場所にデータ保護キーを保管することで解決できるようです。
保護キーの保管先はデータベースやRedisキャッシュなども選択できますが、今回はAzure Blob Storageを利用することとします。
また、保護キー自体を保護するための暗号化キーをAzure Key Vaultで管理するようにします。
コードの修正
まずは、ソースコード側でデータ保護の構成を行います。
下記ライブラリをNuGetでインストール後、DataProtectionのミドルウェアを下記の様に構成します。
- Azure.Extensions.AspNetCore.DataProtection.Blobs
- Azure.Extensions.AspNetCore.DataProtection.Keys
- Azure.Identity
using Microsoft.AspNetCore.DataProtection;
using Azure.Identity;
using Azure.Storage.Blobs;
public class Startup
{
//....
public void ConfigureServices(IServiceCollection services)
{
//....
//アプリケーション設定からBlobコンテナのURLとKey Vaultのキー識別子を取得
var blobContainerUri = configuration["DataProtectionBlobContainerUri"];
var keyIdentifier = configuration["DataProtectionKeyIdentifier"];
//Blobコンテナクライアントを作成
var blobContainer = new BlobContainerClient(
new Uri(blobContainerUri), new DefaultAzureCredential());
blobContainer.CreateIfNotExists();
var blobClient = blobContainer.GetBlobClient("keys.xml");
//サービスに登録
services
.AddDataProtection()
.PersistKeysToAzureBlobStorage(blobClient)
.ProtectKeysWithAzureKeyVault(new Uri(keyIdentifier), new DefaultAzureCredential());
}
}
Azure StrageのBlobコンテナの作成
下記の手順でデータ保護キーを格納するためのBlobコンテナを作成します。
Strageアカウントの作成
まずはAzureポータルにログインし、 Azure Strageアカウントを作成します。
- Standard(汎用v2アカウント)
- geo冗長ストレージ(GRS)
Blobコンテナの作成
作成したストレージアカウントにコンテナを追加します。
- パブリックアクセスレベルは「プライベート」とします。
BlobコンテナのURLを取得
作成したコンテナのプロパティを開き、URLをコピーしておきます。(後ほどこのURLをAppSerciceのアプリケーション設定に登録します。)
Key VaultでKeyを作成する
次に、データ保護キー自体を保護ずるためのキーをAzure Key Vaultで作成します。
Key Vaultコンテナの作成
キーの作成
キーの種類、サイズを指定してキーを作成します。
キー識別子の取得
ここで表示されるキー識別子をコーピーしておきます。(後ほどこのURLをAppSerciceのアプリケーション設定に登録します。)
また、許可された操作は下記のみにチェックが入っている状態に変更して「保存」します。
- キーを折り返す
- キーの折り返しを解除
これで、データの保管先の設定ができました。次に、AppSerciceがこれらのリソースにアクセスできるように設定する必要があります。
App Sercice からデータ保護のリソースにアクセスする
※ ここでは、既にApp Serciceが作成されていて、Asp.NETCoreアプリがデプロイされていることを前提としています。
App Serviceのシステム割り当てマネージドIDを有効にする
Azure ポータルで対象のApp Serciceを開きます。左のメニューでIDを選択し、「システム割り当て済み」タブで「状態」をオンに設定します。
Blobコンテナのアクセス制御でApp ServiceのマネージドIDに対しロールを割り当てる
Azureポータルで、作成したStrage Blobコンテナを開き、左のメニューで「アクセス制御(IAM)」を選択し、「ロールの割り当て」タブをクリックします。
ロールの割り当て追加で「ストレージBLOBデータ共同作成者」のロールを選択します。
アクセスの割当先を「マネージドID」にして、メンバーを選択するをクリック。マネージドIDのプルダウンで対象のAppServiceを選択して割り当てを追加します。
Keyコンテナのアクセス制御でApp ServiceのマネージドIDに対しロールを割り当てる
KeyVaultのKeyコンテナに対しても同様にロールを割り当てます。
作成したKeyVaultのコンテナで「アクセス構成」を選択し、アクセス許可モデルを「Azureロールベースのアクセス制御」に設定後、「アクセス制御(IAM)」
アクセスの割当先を「マネージドID」にして、メンバーを選択するをクリック。マネージドIDのプルダウンで対象のAppServiceを選択して割り当てを追加します。
App Serviceのアプリケーション設定にBlobコンテナのURLとKeyコンテナのキー識別子を設定する
ソースコードを修正した際、アプリケーション設定(Configuration)から下記情報を取得するようにしていました。
名前 | 値 |
---|---|
DataProtectionBlobContainerUri | {BlobコンテナのURL} |
DataProtectionKeyIdentifier | {Key VaultのKey識別子} |
この値をApp Serviceのアプリケーション設定に登録します。
確認
これで一通りの設定が完了しました。
設定したApp ServiceのWebページにアクセス後、Keyコンテナにキーファイルが保存されていればOKです。
参照