AzureのマネージドIDはネットワークを絞るのに役立つか
はじめに
近年、AWSではIAMロールをインスタンスに割り当てたり、AzureではマネージドIDを使うことで、シークレットをコード内で処理することなく認証を受けられるような仕組みが一般的になりました。
また、上の話とは別の要件として、ちょっとセキュリティを意識したシステムを作ろうとすると、システム内から外(インターネット)への通信は極力なしにしたい、という話が出てきます。
2つの話を同時に満たすために、「Azure VMでマネージドIDを使えば、インスタンスメタデータサービスから情報を受け取れるので、もしかしたらインターネットとの通信は遮断しても良いのでは!?」と期待して、検証して、ダメだった話を残したいと思います。
常識なのかもしれませんが、だってググっても出てこなかったんですよ…。
前提知識
マネージドIDとは
わざわざこの記事にたどり着くくらいの方ですので、マネージドIDとは何ぞや、というのは釈迦に説法かと思いますが…。
マネージドIDは、VMやApp Serviceなどに割り当てられるAzureAD上のID (ServicePrincipal)です。このIDに、Azureリソースロールを付与することで(=サブスクリプションやリソースをスコープとしてロールを付与することで)、VMやApp ServiceはAzureADの認証を受ける⇒Azureリソースやデータに対するアクセス権を得る、ということを簡単にできるようになります。
処理リソース側(VMやApp Service)で動くコード上は、パスワードのようなシークレットを意識することなく、自分に割り当てられているマネージドIDに付与されている権限を行使することができます。うっかりクレデンシャル情報をログに残さないように気を付けたり、暗号化して格納しておいたり、ローテーションさせたり…という管理が不要になるわけですね。革命的な仕組みです。
インスタンスメタデータサービスとは
ココから先はAzure VMを例に説明していきます。各VMには、そのインスタンス自身からしか見えない「インスタンスメタデータサービス(IMDS)」という領域があります。
VMからは「169.254.169.254」といういう特殊なIPアドレスで見えているのですが、IMDSには自インスタンスの構成情報などが含まれており、よくある用途としては「メンテナンスの情報を受け取って、自サーバー内の処理を止めてメンテナンスReadyにする」といった話を聞きます。
IMDSの中には「マネージドID」の情報が含まれており、VMからはインスタンスメタデータから情報を貰うことで、AADの認証を受けることができる…とされています。
ウッカリ騙されそうになった話
マネージドIDについて調べていると、簡略化された、このようなイメージ図に出会うことがあります。これを見ると、VMはインスタンスメタデータサービスとのみ会話できれば良さそうに見えます。
が、検証してみたらやっぱりそうではありませんでした…というのが今回の話です。
検証
作った環境
完成図は下記の環境です。
testVMはWindows Server、testlinVMはCentOSでしたが、今回はWindows Serverで検証しています。
また、マネージドIDを使った認証ができるか確認するために、VMにはPowershellのAzモジュールと、Azure CLIをインストールしています。
Bastion(202110-bastion)
検証内容には直接関係ありませんが、VMに入るための経路として用意しました。VMにパブリックIPを賦与して直接入ってもいいのですが、そもそもやりたい検証(セキュアにマネージドIDで認証を受けたい!)の意図から外れてしまいますので。
VM環境のサブネット(VMsubnet / VMtest-NSG / testRT)
NSGを使って、インターネットへのアウトバウンドを塞ぐことができるようにしています。
また、ルートテーブルを作成し、インターネット向け(0.0.0.0/0向け)の通信のネクストホップをAzure FWに向けています。
AzureFW(testfw)
接続元をVMsubnetとし、接続先URLをフィルタリングできるようにAzureFWのアプリケーションルールを設定します。
試したコト
AzureFWでインターネットアクセスを塞いだ時
上記環境で、まずAzureFWのルールで穴あけをせず、VMからインターネットに抜けられないようにします。
VM上で、MangedIDを使ったログインコマンドを実行します。
az login --identity
結果は下記の通り。NGです。
NSGでインターネットアクセスを塞いだ時
次に、NSGで0.0.0.0/0向けの通信をDenyしてみます。
結果は以下の通り、プロンプトが返ってきません。(ずっと待てばタイムアウトすると思いますけど)
必要インターネット通信を通した時
最後に、NSGを空けて、AzureFWに必要な通信を通すルールを設定します。
ログインに必要な通信先は下記の記事を参考に設定するものとし、
下のようなルールとしました。
もうお分かりの通り、結果としてはログイン可能でした。
分かったコト
ManagedIDを使ったときの通信イメージは、下のようになっている…ということが分かりました。
また、下記の公式ドキュメントにも解説が書いてあり、
手順 3. で構成したクライアント ID と証明書を使用して、手順 5. で指定したアクセス トークンを要求する呼び出しが Azure AD に対して行われます。 Azure AD は、JSON Web トークン (JWT) アクセス トークンを返します。
と明確にVM⇔AzureAD間の通信が必要であることが書かれていました。分かってから読むと確かに書いてある…
おわりに
マネージドIDを使うことでネットワークも制限してよりセキュアに使えるのではないか!?と期待して検証してみました。しかし、VMからインターネット(ログイン認証に必要な範囲)の通信は塞ぐことはできず、ある程度の穴あけは必要です。
マネージドIDを解説されているいろんなサイトでもこの仕様について言及されている情報があまりなかったように思いますので、迷ってたどり着かれた方の助けになれば幸いです。(もしかして誰も困ってない?)
Discussion
証明書を利用してトークンを取得するのはあくまで、メタデータ エンドポイント (IMDS) であり、VM から AAD への疎通は必ずしも必要ありません。実際に Outbound を完全にブロックした状態でも curl で直接トークンを取取得できます。
az login --identity が失敗する理由は、トークン取得後に Azure REST API (management.azure.com) を呼び出すからであり、AAD への通信が行えないからではないかと。
なるほど、確かに最後のFWルールの穴あけをしているところで、「login.microsoft.com」や「management.azure.com」を同時に空けてしまっているので、検証になってませんでしたね…。
ありがとうございます!ちゃんと確認して訂正しないといけないですね…