Managed Service Identity (Preview)

Managed Service Identity(MSI)(Preview)というのが出ました。

概要

Managed Service Identity(以下MSI)はざっくりいうと、アプリケーションがアプリケーションとしてAzureにアクセスするための資格情報(アクセストークン)をお手軽に取得するための仕組みです。あと大事なところですが、単にお手軽にするだけでなく、シークレットキーなどの重要な値をコードから除外するのが目的です。

Azureの各サービスのREST API(ARMのAPI)を呼び出すためにはアクセストークンが必要ですが、通常以下のようにアクセストークンを取得するための準備がやや大変です。

1. サービスプリンシパルなど登録(IDやシークレットキーを登録して控えたりする)
2. アクセス先の対象に権限を付与
3. コード内でIDやキーを使ってアクセストークンを取得する処理を実装

だいたい1が面倒くさくて3のキーの扱いで悩むわけです。
さてMSIを使うとざっくり以下のようになります。

1. MSIを有効にする
2. アクセス先の対象に権限を付与
3. コード内でMSIを使って認証トークンを取得する処理を実装

まず1番目のもろもろの登録がシンプルになりました。機能をOnにするだけです。
さらにシークレットキーなどの重要な情報も管理下から離れました。(重要)
もろもろ必要な設定周りをAzureに任せて(Managed)キーの管理を(設定ファイルなどから)除外することができますね。

使い方

MSI機能をOnにするのは実に簡単です。ポータルから設定する場合は設定ブレードのManaged Service IdentityをOnにして保存するだけです。(VMの場合はConfiguration、App Serviceの場合はManaged Service Identity、Functionsの場合はPlatform FeatureのManaged Service Identity)

image
※機能をOnにした後、App ServiceとFunctionsは再起動したほうが良いかも(環境変数に必要な情報が入るので読み込ませるために)

ポータルからOnにした後はアクセス対象に権限を付ける必要があります。Azure(のARM)はロールベースで権限設定もできますしリソース単位で個別に権限付与もできます(RBAC)ので、適切にアプリケーションに対してだけ許可したりすることができます。さてサービスプリンシパルなどで権限付与する場合はご存知の通りですが、MSIの場合は明示的に作ってません。でも心配無用です。Azureポータルだとリソースのアクセスコントロール(IAM)から付与できるようになっています。

App Service/FunctionsでMSIを有効にした場合、「Azure AD user, group or application」の一覧にでてきます。※明示的に名称を入れないと一覧にでないので注意しましょう。
image
VMの場合は「Virtual Machine」を選択すればVMを選べます。
image

ロールの割り当てなどはユーザーやグループなどと同じなので問題ないですね。
察しの通り、同じAzure ADテナントでないと選択できないのでダメです。(テナント跨いだ設定はまだ難しい?)

これで下準備はOKです。さてアクセストークンの取り方はざっくり以下のような感じです。

1. MSIのエンドポイントにアクセス先のリソース(URL)を渡す。
2. MSIのエンドポイントからJSONでアクセストークンを含んだレスポンスが返ってくるのでaccess_tokenを抜き出す。

簡単ですね。MSIのエンドポイントへのアクセス方法ですが、VMとApp Service/Functionsで少しだけ違うので注意しましょう。

VMの場合

VMの場合はMSIエンドポイントは固定で http://localhost:50342/oauth2/token です。URL引数にresourceでアクセス先のリソースを指定してGETします。
例:
image

> $response = Invoke-WebRequest -Uri "http://localhost:50342/oauth2/token?resource=https://management.azure.com/" -Method GET -Headers @{Metadata="true"}
> $content = $response.Content | ConvertFrom-Json
> $content

access_token  : eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkhIQnlLVS0wRHFBcU1aaDZaRlBkMlZXYU90ZyIsImtpZCI6IkhIQnlLVS0
<省略>
                xPuM3Te4q_YiCBU82UF0oa2wQ9z5BDmGF73AXf0KxkhlQ
refresh_token :
expires_in    : 3599
expires_on    : 1505922456
not_before    : 1505918556
resource      : https://management.azure.com/
token_type    : Bearer

App Service/Functionsの場合

App Service/Functionsの場合はエンドポイントは不定です。実際のエンドポイントを取得するには環境変数からゲットします。MSIは MSI_ENDPOINT 環境変数にエンドポイントのURLを、 MSI_SECRET にエンドポイントにアクセスするためのキーが入ります。これらを使ってリクエストを投げると同様にアクセストークンを含んだレスポンスを取得することができます。

Functionsでの例:

using System;

public static async Task Run(string input, TraceWriter log)
{
    var endpoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT");
    var client = new HttpClient();
    var resource = "https://management.azure.com/";
    var apiversion = "2017-09-01";
    client.DefaultRequestHeaders.Add("Secret", Environment.GetEnvironmentVariable("MSI_SECRET"));
    var res = await client.GetAsync(String.Format("{0}/?resource={1}&api-version={2}", endpoint, resource, apiversion));
    log.Info(await res.Content.ReadAsStringAsync());
}

リクエストヘッダにSecretでMSI_SECRETの値を設定してURL引数にresourceとapiversionを指定するだけです。apiversionは今のところ 2017-09-01 固定ですかね。

取得したレスポンスのaccess_tokenにJWTトークンがBearer用として入ってますので、AzureのREST APIなどのAuthorizationにそのまま使えます。
image

ちゃんとアクセスできますね。
これで最低限OKなんですが、MSIの意義の1つであるコードからキー情報などを含めたくないという話がありました。最近だとKey Vaultを使って秘密鍵などを分離保管するケースが多いのですが、じゃぁそのKey Vaultにアクセスするキーはどうするんだ?という話になりますよね。そこでMSIの出番というわけです。
MSIでKey Vaultにアクセスするためのアクセストークンを取得して後は各リソースへのキーなりをKey Vaultから取得しましょうという感じです。

今のところ.NET限定だと思いますが、Key Vaultの最新NuGetパッケージを使っておけば上記のエンドポイントやKey Vaultへのアクセストークンを取得する部分を大幅に省略させることができます。

コード例:

var azureServiceTokenProvider = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await kv.GetSecretAsync("https://<Your Key Vault>.vault.azure.net/secrets/<Target>");

image

これだけでMSIからアクセストークンを取得してKey Vaultにアクセスできるようになります。VM/App Service/Functionsで共通して動作するのでバッチリですね。これでコード内から秘匿したいキー情報が除けました。(Key Vaultの接続先だけアプリケーション設定なりに含むことになりますけど)
単にコードからキー情報が無くなるだけでなく、厳密にアクセス対象と権限を開発から分離して管理も委譲したりできるのでメリットは大きいですね。またブートストラップ時のキーの扱いに対しても一定の解がこれで出た感じはしますね。

注意点

さていくつか注意点があります。

MSIからもえらえるアクセストークンの有効期間

MSIからもらえるアクセストークンは1時間有効です。またある程度MSI側でキャッシュしてくれるようなのですが、(無いとは思いますが)MSIから得たアクセストークンを使いまわす場合は注意しましょう。

MSIでもらえるアクセストークンの利用先(リソース)

現時点ではAzureのManagement REST API(ARM)、Key Vault、Data Lakeの3種類だけとなります。それ以外のリソースへのアクセストークンは取得できませんので注意ください。

なおMSIのresourceに渡す場合はケースセンシティブで最後の/までちゃんと含める必要があります。

ローカルでの開発時

Key Vaultを使う場合(先に挙げたコード)、ローカル開発時は内部でAzure CLIを呼び出しアクセストークンを取得します。Azure CLI 2.0.12以降をインストールしてアプリケーション実行前にaz loginなどでログインしておきましょう。(Azure CLI 2.0.12以降だとaz account get-access-token でアクセストークンを取得できます)

※うまく動作しない場合はAzure CLIをアプリから認識できていない可能性があるので AzureCLIPath 環境変数にインストール先フォルダ ( C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin など)を設定しておくといいようです。

また対象Azure ADテナントにAzure AD JoinとかしているPCとアカウントの場合はWindows統合認証で認証できるようです。

それ以外の場合は代替コード書くなりする必要がありそうですね。(この辺はMSI使わない場合と大差ないかな)※ https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet/

Running the application using a service principal in local development environment も参照ください。

Deep Diveぽいやつ

とりあえず使う分にはだいたい上記までの内容で把握できるかと思います。(あとは参考資料を参照ください)
仕組み的にはMSIの拡張モジュールが単にアクセストークンを取得する処理を行うWebサービスというだけなのですが、Managedとして動くようにポータルから設定する裏であれこれ処理されています。
通常アクセストークンをもらうために誰がという情報(ユーザーやサービスプリンシパル)が必要になりますが、MSI使っていると見えません。かろうじてRBACの割り当ての時に出てくるぐらいですね。
今現在のAzureポータルのAzure ADブレードだと表示されませんが、内部的にはAzure ADに情報が登録されてるぽいです。(違うかも知れないけど扱い的にはそう)

MSI機能を有効にすると、ARM上以下のプロパティが追加されます。

VMの場合:
image
App Service/Functionの場合:
image

tenantId はAzure ADテナントのGUIDですね。MSIやRBACはこの値を使用して判断します。ちゃんとAzure ADに登録されているのでIAM(RBAC)の対象として選択できるようになります。

ARMテンプレートでデプロイする場合、”identity”: { “type”: “SystemAssigned” }  を追加しておけば上記tenantIdとprincipalId が展開後に設定されます。これらの値を使ってKey Vaultにアクセス権を設定することもできます。( https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet/blob/master/azuredeploy.json#L99 を参考に)

まとめ

というわけでMSI使うとだいぶいい感じになると思います。

参考

Managed Service Identity (Preview)」への2件のフィードバック

  1. ピンバック: Azure Update (2017.09.21) | ブチザッキ

  2. ピンバック: Visual Studioで開発時にMSIを使う | ブチザッキ

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中