みなさん、こんにちは。サイオステクノロジー武井です。今回はAzureでIDや資格情報を管理する上で必須であるマネージドIDについて、世界一わかりみ深い説明を試みてみたいと思います。
目次
マネージドIDとは?
なかなか一言では言い表せませんが、強いて言うならば、仮想マシンなどのリソースからのみアクセスできる特定のエンドポイントにアクセスして資格情報(サービスプリンシパル)を取得して、そのサービスプリンシパルでいろいろな他のリソースにアクセスするというものです。
なかなかにこれだけの説明ではイメージが湧きにくいと思いますので、ここでは「マネージドIDがない世界」と「マネージドIDがある世界」のID管理を比べてみることで、理解の一助になれば幸いです。
マネージドIDの超ザックリな説明
マネージドIDはちょっと複雑ですので、まずはイメージを掴むために、超ざっくりとしたマネージドIDの説明をしたいと思います。超ざっくりなだけに一部デフォルメした説明になっていることをご了承くださいませ。超ざっくりな説明をした後には、詳細な説明をしますので、そこでリアルな動作を把握頂ければと思います。
では、超ザックリな説明で、「マネージドIDがない世界」と「マネージドIDがある世界」を比べてみます。
マネージドIDがない世界
とあるAzureサービスに接続するためのプログラムを考えてみます。Azureサービスに接続するためには当然認証が必要でして、IDやパスワードが必要になります。以下のようにGitHubでプログラムのソースコードを管理して、本番環境にデプロイする例を考えてみます。ちなみに開発者はVMの中にはSSH等でアクセスできる権限はないものとします。あくまでFTPやSCPなどと言った手段でプログラムをデプロイする権限しかありません。
①では、Azureサービスに接続するためのプログラムのソースコードをGitHubに登録するわけですが、AzureサービスにアクセスするためのIDやパスワードもソースコードの中の設定ファイルに書いてGitHubに登録します。
②では、開発者はGitHubからソースコードを持ってきて本番環境にデプロイします。
③では、本番環境にデプロイされたプログラムが、IDやパスワードを使ってAzureサービスに接続します。
よくある一般的な流れですよね。
これがマネージドIDがない場合のIDやパスワードの管理方法です。では、この方法の何がまずいのでしょうか?
ここで、開発者が実は悪い人だったとします。
開発者はもちろんGitHubにはアクセスできるわけで、このGitHubにはIDやパスワードなどの資格情報が入っているので、それを利用して直接Azureサービスにアクセスして本番情報を引っこ抜いて悪用ができるわけですね。
マネージドIDを使うと、ソースコードからIDやパスワードなどの資格情報を排除して、このような不幸な事態を防ぐことができます。
マネージドIDがある世界
マネージドIDがある世界では、以下のような動きになります。ちなみにここでも開発者はVMの中にはSSH等でアクセスできる権限はないものとします。あくまでFTPやSCPなどと言った手段でプログラムをデプロイする権限しかありません。
①では、開発者よりもとても強い権限を持ったシステム管理者が、Azure Instance Metadata ServiceにIDやパスワードを登録します(実際の動きではシステム管理者が直接Azure Instance Metadata Serviceにアクセスすることはないですが説明を簡略化するためにこのように記述しています)。
実はここでAzure Instance Metadata Serviceに登録したIDが、マネージドIDと言われるものです。
②では、本番環境のVMがAzure Instance Metadata Serviceにアクセスして、IDやパスワードを取得します。ここが一番重要なところなのですが、このAzure Instance Metadata Serviceはアクセス元のVMを識別して、そのVM固有の情報(仮想マシン名やリージョン情報など)を返す特殊なサービスです。
Azure Instance Metadata ServiceのIPアドレスは169.
要は、あるVMの情報をAzure Instance Metadata Serviceから取得したい場合は、絶対にそのVM自身からAzure Instance Metadata Serviceにアクセスする必要があるというわけです。AというVMからAzure Instance Metadata Serviceにアクセスしても、BというVMの情報を取れることは絶対にありません。
そして、Azure Instance Metadata Serviceから返される情報には、先程①で登録したIDやパスワードなどの情報も含まれます。
③では、プログラムがAzure Instance Metadata ServiceにアクセスしてIDやパスワードを取得して、Azureサービスに接続します。
またもや、ここで開発者が悪い人だったとしましょう。
開発者はIDやパスワードを盗もうと試みますが、GitHubにはありません。Azure Instance Metadata Serviceにはありますが、このID・パスワードは本番環境のVMからしか取得できないので、VMの中にアクセスできない開発者がAzure Instance Metadata Serviceの中にあるIDやパスワードを取得できません。
このようにして、マネージドIDを使うと、ソースコードからIDやパスワードをなどの資格情報を取り除くことができて、悪い開発者から機密情報を守ることができるのです。
マネージドIDの詳しい説明
では、ここからはマネージドIDの詳しい説明をしていきます。先程の「超ザックリな説明」では、わかりやすさを優先するため、いささか説明を省略したりしていましたが、本章では「超ザックリな説明」では説明しきれなかった詳細な部分まで説明いたします。
ということで「超ザックリな説明」と同じように「マネージドIDがない世界」と「マネージドIDがある世界」を比べてみます。
マネージドIDがない世界
「超ザックリな説明」よりもさらに具体的にするために、Azure Blob Storage(Azureが提供するマネージドなストレージサービス)に接続する例に考えてみます。
Azure Blob Storageに接続する方法は色々あります。アクセスキー・SAS(Shared Access Signature)・サービスプリンシパルと3つもあるのですが、ここではサービスプリンシパルによる接続方法を例に考えてみます。サービスプリンシパルのほうが説明がわかりやすくなると思います。
前置きが長くなりましたが、マネージドIDがない世界では、Azure Blob Storageに接続するためには以下の図に書いてあるような方法を取ることになります。ちなみに開発者はVMの中にはSSH等でアクセスできる権限はないものとします。あくまでFTPやSCPなどと言った手段でプログラムをデプロイする権限しかありません。
①では、システム管理者がAzure ADにサービスプリンシパルを登録します。サービスプリンシパルとは、アプリケーションのためのIDになります。人間(ユーザー)がサービス(Office365とか)に接続するためには、Azure ADにユーザーを登録します。それと同じ意味合いで、アプリケーションやプログラムがサービス(Office365とか)に接続するためには、Azure ADにサービスプリンシパルを登録します。つまり、ユーザーは人間のためのID、サービスプリンシパルはアプリケーションのためのIDというわけです。このあたりの詳細は、このブログではとても語り尽くせないので、以下を参照いただけますと幸いです。
②では、開発者がGitHubにソースコードをプッシュします。そのソースコードには、Azure Blob Storageに接続するための情報であるクライアントIDとクライアントシークレットが記載されています。このクライアントIDとクライアントシークレットについては後述します。
③では、開発者が②プッシュしたソースコードをプルします。本番環境にデプロイするための準備作業です。
④では、②でGitHubからプルしたソースコードを本番環境にデプロイします。
⑤では、プログラムがクライアントIDとクライントシークレットをAzure ADに渡します。ユーザーが認証するためには、IDとパスワードが必要ですよね。サービスプリンシパルはアプリケーションのためのIDというのは先に説明したとおりですが、アプリケーションにとってのIDとパスワードが「クライアントID」「クライアントシークレット」になるわけです。このあたりは先にご紹介したブログに詳細が書いてありますので、ご覧いただけますと幸いです。
⑥では、正しいクライアントIDとクライアントシークレットを渡すと、プログラムは「トークン」を受け取ります。このトークンによって、プログラムはいろいろなサービス(Office365とか)にアクセスします。ユーザー(人間)の場合は、IDとパスワードがあっていれば即サービスにアクセスできますが、サービスプリンシパルは一旦トークンというものを受け取ります。
⑦では、プログラムがサービス(ここではAzure Blob Storage)にトークンを渡してアクセスします。トークンが本当にAzure ADから発行された正しいものかどうかをサービス側では検証するわけですが、これについて語りますと、少々長くなりますので今回は割愛させて頂きます。
というのが「マネージドIDがない世界」での、Azure Blob Storageに接続するまでの一連の流れです。
文字だけの説明もあれですので、この流れを実践してみたいと思います。まずはサービスプリンシパルの作成です。
まずAzure ADにアクセスして、「アプリの登録」→「+新規登録」の順にクリックします。
「名前」にはサービスプリンシパルを一意に識別する任意の名称を入力します。「サポートされているアカウントの種類」は「この組織ディレクトリのみに含まれるアカウント」をチェックします。そして最後に「登録」をクリックします。
以下の画面に遷移しますので、「アプリケーションID」「ディレクトリID」をメモっておきます。
左部メニューから「証明書とシークレット」→「+新しいクライアントシークレット」の順にクリックします。
「説明」はこのシークレットを説明するわかりやすいもの、有効期限はその名の通りシークレットの有効期限ですが、ここでは6ヶ月としておきます。
シークレットの値が自動生成されますので、メモっておきます。
次に接続を許可したいAzure Blob Storageに移動して、先程作成したサービスプリンシパルから接続ができるように権限設定をします。「アクセス制御(IAM)」→「追加」→「ロールの割り当ての追加」の順にクリックします。
「ストレージBLOBデータ共同作成者」を選択して「次へ」をクリックします。
「+メンバーを選択する」をクリックします。
「選択」のテキストボックスのところに、先程作成したサービスプリンシパルの名称を入力しますと、候補として先程作成したサービスプリンシパルが表示されますので、選択して「選択」をクリックします。
先程選択したサービスプリンシパルが表示されていることを確認して「レビューと割り当て」をクリックします。
内容に問題ないことを確認して、もう一度「レビューと割り当て」をクリックします。
これでサービスプリンシパルは出来上がりました。以下のコマンドを実行してトークンを取得します。
$ curl -X POST https://login.microsoftonline.com/[先程メモしたディレクトリID]/oauth2/token -F grant_type=client_credentials -F resource=https://storage.azure.com/ -F client_id=[先程メモしたアプリケーションID] -F client_secret=[先程メモしたクライアントシークレット] | jq -r .access_token
では以下のコマンドを実行すると、Azure Blob Storageの特定のコンテナ配下にあるファイルのリストを取得できます。
$ curl -X GET -L -H "Content-Type: application/json" -H "Authorization: Bearer [先程メモしたトークン]" -H "x-ms-version: 2020-10-02" "https://[ストレージアカウントの名前].blob.core.windows.net/images?restype=[コンテナー名]&comp=list"
こんな感じでサービスプリンシパルでAzureのサービスにアクセスすることができました。
では、「マネージドIDがない世界」では、一体何が問題なのでしょうか?
実は開発者が悪い人だったとします。この開発者はGitHubにプッシュされているクライアントIDとクライアントシークレットにはアクセスできますので、先程の要領でAzure Blob Storageにはアクセスできてしまいます。ということは本番環境の大事なファイルを取り放題なわけですね。これはあまりよくありません。
マネージドIDがある世界
では、「マネージドIDがある世界」では「マネージドIDがない世界」で生じていた問題がどのように解決されるかを説明します。
「マネージドIDがある世界」では以下のようにAzure Blob Storageにアクセスします。ちなみにここでも先ほどと同様に、開発者はVMの中にはSSH等でアクセスできる権限はないものとします。あくまでFTPやSCPなどと言った手段でプログラムをデプロイする権限しかありません。
①では、システム管理者がAzureポータルからマネージドIDを有効にします。
②では、①でマネージドIDを有効にした結果、Azure Resource Manager(Azureのすべてのリソースを管理するサービス)がAzure ADにサービスプリンシパルを作成します。
③では、②でサービスプリンシパルを作成したときに用いたクライアントIDと証明書をAzure Resource ManagerがAzure Instance Metadata Serviceに登録します。Azure Instance Metadata Serviceについては「マネージドIDの超ザックリな説明」の「マネージドIDがある世界」も説明しました。けれども、大事なことですので、くどいようですがもう一度説明します。
Azure Instance Metadata Serviceはアクセス元のVMを識別して、そのVM固有の情報(仮想マシン名やリージョン情報など)を返す特殊なサービスです。
Azure Instance Metadata ServiceのIPアドレスは169.
要は、あるVMの情報をAzure Instance Metadata Serviceから取得したい場合は、絶対にそのVM自身からAzure Instance Metadata Serviceにアクセスする必要があるというわけです。AというVMからAzure Instance Metadata Serviceにアクセスしても、BというVMの情報を取れることは絶対にありません。
そして、Azure Instance Metadata Serviceから返される情報には、先程③で登録したクライアントIDや証明書などの情報も含まれます。
また、「マネージドIDの超ザックリな説明」の「マネージドIDがある世界」では、Azure Instance Metadata Serviceに登録されるのはIDとパスワードと書きました。説明を簡略化するためにこのように書いたのですが、実際は先の図のようにAzure Instance Metadata Serviceに格納されるのはサービスプリンシパルにアクセスするためのクライアントIDと証明書になります。
④では、システム管理者がサービスプリンシパルに権限を与えます。これによりこのサービスプリンシパルがAzure Blob Storageにアクセスできるようになります。つまりマネージドIDは作っただけではダメで、適切な権限を与えてあげないとダメというわけです。マネージドIDがサービスプリンシパルということを考えれば当然のことになります。
⑤では、VMの中のプログラムは、Azure Instance Metadata Serviceにアクセスします。
⑥では、Azure Instance Metadata Serviceは、自身の中に保存してある資格情報(クライアントID、証明書)を用いてAzure ADに接続します。
⑦では、⑤の結果としてトークンを受け取ります。この⑤、⑥の流れは「マネージドIDの詳しい説明」の「マネージドIDがない世界」の⑤、⑥と同じです。
⑧では、Azure Instance Metadata は、Azure ADから受け取ったトークンをそのままVM内のプログラムに返します。
⑨では、先程取得したトークンでAzure Blob Storageにアクセスします。
ここまでの一連の流れでご説明したとおり、マネージドIDの実態はサービスプリンシパルということです。AzureポータルからマネージドIDを有効にすると、Azure Resource Managerを通じて、Azure ADにサービスプリンシパルを作成、合わせてAzure Instance Metadata Serviceに資格情報(サービスプリンシパルにアクセスするためのクライアントIDと証明書)を作成するわけです。そしてAzure Instance Metadata Service内の証明書は自動で更新がされます。まさにマネージド!!
一通りマネージドIDを使ったAzureサービスへのアクセス方法を説明したのですが、マネージドIDのメリットをお伝えするために、「マネージドIDがない世界」との比較をしてみたいと思います。
では、またもやここで、開発者が悪い人だったとします。
開発者は、なんとかAzure Blob Storageから情報を盗み出そうとします。ただし「マネージドIDがない世界」のときのようにGitHubにはクライアントIDとクライアントシークレットはありません。証明書(クライアントシークレットに変わる情報)はAzure Instance Metadata ServiceとAzure ADの中にしかありません。
Azure Instance Metadata Serviceは先程申し上げましたように、VMの中からしかアクセスができません。開発者はVMの中にはSSHなどでアクセスできる権限はないので、事実上Azure Instance Metadata Serviceにはアクセスができません。
Azure ADにはシステム管理者しかアクセスができず、開発者はアクセスする権限がありません。
結局開発者は本番環境のクライアントIDや証明書を取得できないので、Azure Blob Storageからデータを盗み出すことができません。
これまでの説明でわかりましたように、大切な本番環境への接続情報を開発者に一切知らせることなく、開発者に開発をお願いすることができるのです。システム管理者もこれで安心ですね。また、不意にGitHubなどのリポジトリに資格情報がプッシュされて、ひょんなことからその資格情報が漏れて大騒ぎなんてリスクもありません。
このように、マネージドIDを使うとソースコードにIDやパスワードなどの資格情報を書く必要がまったくなくなるのです。
すごいですね、マネージドID!!
実際にマネージドIDを使ってソースコードから資格情報を追い出して見ようと思います。百聞は一見にしかずということで実際に手を動かしてやってみましょう。
VMのリソースにアクセスをして、左部メニューから「ID」をクリックします。そして、右部にマネージドIDの詳細が表示されます。「システム割り当て済み」「ユーザー割り当て済み」の2つがあるのですが、ここでは「システム割り当て済み」を選択して「状態」を「オン」にして、「保存」をクリックします。
上記の作業は先に紹介した下図の①の作業に相当します。この操作でAzure Resource Managerに「マネージドIDを有効にしたよー」って伝えることができます。
さて、これでAzure Resource ManagerはAzure ADにサービスプリンシパルを作成してくれているはずです。先と同じように、VMのリソースにアクセスをして、左部メニューから「ID」をクリックしますと、右部に「オブジェクト(プリンシパル)ID」なるものが見えます。これはサービスプリンシパルのIDになりますので、これをメモっておきます。
ということで、本当にマネージドID(サービスプリンシパル)ができているかどうかを確認してみましょう。Azure ADのリソースにアクセスして、「エンタープライズアプリケーション」をクリックします。
左部メニューの「すべてのアプリケーション」をクリックして、画面右部の「アプリケーションの種類」にて「マネージドID」を選択していますと、先程作成したマネージドID(サービスプリンシパル)が表示されます。VMからマネージドIDを作成した場合は、VMの名前でマネージドIDができるようです。
マネージドIDは作成しただけでは役に立ちません。マネージドIDの実態はサービスプリンシパルなので、適切な権限を付与してあげる必要があります。早速やってみましょう。実はここからの手順は「マネージドIDがない世界」で行ったものとほとんど変わりはありません。
「アクセス制御(IAM)」→「追加」→「ロールの割り当ての追加」の順にクリックします。
「ストレージBLOBデータ共同作成者」を選択して「次へ」をクリックします。
「アクセスの割り当て先」で「マネージドID」をチェックして「+メンバーを選択する」をクリックします。すると画面左部に新しいブレードが出てくるので、「マネージドID」のセレクトボックスで「全てのシステム割り当てマネージドID」を選択します。すると、先程作成したマネージドID(作成したVMと同じ名称のもの)が表示されますので、それを選択して「選択」をクリックします。
「レビューと割り当て」をクリックします。
「レビューと割り当て」をクリックします。
これでマネージドIDの設定は完了しました。では、実際にマネージドIDを使ってAzure Blob Storageにアクセスしてみましょう。
では、「マネージドIDがない世界」と同様に、Azure Blob Storageにアクセスするためのトークンを取得します。以下のコマンドを実行します。
$ curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fstorage.azure.com%2F' -H Metadata:true -s {"access_token":"[アクセストークン]","client_id":"XXXX-XXXX-XXXX-XXXX","expires_in":"86400","expires_on":"1642429086","ext_expires_in":"86399","not_before":"1642342386","resource":"https://storage.azure.com/","token_type":"Bearer"}
上記のコマンドのレスポンスとしてAzure Blob Storageにアクセスするためのトークンが取得できます。これも「マネージドIDがない世界」と同様なのですが、決定的な違いがあります。それは、コマンドの中に「マネージドIDがない世界」のときのように資格情報(クライアントIDとクライアントシークレット)がないことです。
マネージドIDの場合、アクセストークンを取得するためにはAzure Instance Metadata Serviceにアクセスするというのは先述したとおりですが、このアクセス先のIPアドレス(169.
なので、「マネージドIDがない世界」のときのように資格情報(クライアントIDとクライアントシークレット)を渡してあげる必要がないのですね。
後はこのアクセストークンを使って、「マネージドIDがない世界」のときと同じように、以下のコマンドを実行すると、Azure Blob Storageの特定のコンテナ配下にあるファイルのリストを取得します。
$ curl -X GET -L -H "Content-Type: application/json" -H "Authorization: Bearer [先程メモしたトークン]" -H "x-ms-version: 2020-10-02" "https://[ストレージアカウントの名前].blob.core.windows.net/images?restype=[コンテナー名]&comp=list"
いかがでしょうか?マネージドIDを使って、いかにソースコードから資格情報を追い出すことができるかがご理解いただけたかと思います。
いろんなマネージドID
マネージドIDには「システム割り当てマネージドID」と「ユーザー割り当てマネージドID」の2種類があります。
システム割り当てマネージドID
システム割り当てマネージドIDは、それぞれのVMごとでマネージドIDとサービスプリンシパルが作成されます。VMの管理画面からマネージドIDを有効にするだけで、Azure Instance Metadata Serviceの中にマネージドIDが作成されて、さらにAzure ADの中にサービスプリンシパルも作成されます。さらにVMが削除されると、それに紐づくマネージドID、サービスプリンシパルも削除されます。非常にお手軽なではありますが、VMが作成されるごとにサービスプリンシパルが出来上がり、管理が煩雑になるという側面もあります。
ユーザー割り当てマネージドID
ユーザー割り当てマネージドIDは、複数のVMでマネージドID及びサービスプリンシパルが共有されます。システム割り当てマネージドIDと比べて、事前にマネージドIDを作成しなければならなかったりと面倒な部分がありますが、サービスプリンシパルが一つで済むという点ではシンプルな運用になります。
ユースケースとしては、ロードバランサー配下に配置された複数のVMでマネージドIDを使う場合にユーザー割り当てマネージドIDは便利です。この場合にシステム割り当てマネージドIDを使うと、複数のサービスプリンシパルができることとなり、それぞれに権限を与えてあげないといけませんので面倒です(><)
では早速ユーザー割り当てマネージドIDを作成してみましょう。
Azureポータル上部の検索テキストボックスにて、「マネージドID」と入力すると、「マネージドID」というサービス名が表示されるので、それをクリックします。
「+作成」をクリックします。
サブスクリプション、リソースグループ、リージョンは適宜環境にあったものを入力します。「Name」はマネージドIDを識別するわかりやすい名前を入力します。最後に「確認および作成」をクリックします。
内容に問題がなければ「作成」をクリックします。
これでマネージドID及びサービスプリンシパルが作成されました。ちなみに、Azure ADにサービスプリンシパルが作成されているか見てみましょう。Azure ADのリソースにアクセスして、「エンタープライズアプリケーション」をクリックします。
確かに先程作成したユーザー割り当てマネージドIDに対応したサービスプリンシパルができてます。
話は横道にそれましたが、ユーザー割り当てマネージドIDを割り当てたいVMのリソースに移動します。画面左部メニューの「ID」をクリックして、画面右部に表示される「ユーザー割り当て済み」のタブをクリックします。そして「+追加」をクリックします。
先程作成したマネージドIDが表示されているかと思います。もし表示されていなければ、「ユーザー割り当て済みマネージドID」のテキストボックスにマネージドIDの名前を入力しますと表示されます。表示されたマネージドIDを選択して「追加」をクリックします。
すると、以下のような画面に遷移して、ユーザー割り当てマネージドIDが出来上がります。これで作業は完了です。
Azureサービス以外でのマネージドIDの活用
今までの説明では、マネージドIDによる認証・認可対象はAzure Blob StorageなどのAzureサービスでした。マネージドIDは、認証・認可によって、Azure ADからトークンが返されてきて、そのトークンを利用します。つまり、このトークンを解釈できる必要がありますので、必然的にAzureのサービスとなるわけです。
でも、Azureが提供する以外のサービスでもIDやパスワードなどの資格情報使うときってありますよね。GoogleのAPIにアクセスしたりとかです。
つまり、「マネージドIDがある世界」で説明した以下の構成図の、Azure Blob StorageのところをAzure以外のサービスにしたいというわけです。
でも、GoogleのAPIのような外部サービスは当然Azure ADから発行されたトークンは解釈できません。
そんなときはAzureのサービスであるAzure Key Vaultというものを使うと幸せになれます(^^)
Azure Key Vaultとは?
Azure Key Vaultは証明書やパスワードなどの資格情報をセキュアに保存するAzureのサービスです。保存された資格情報が暗号化されるのはもちろんのこと、RBACによる資格情報への細かい認可も行うことが出来ます。
今まではソースコードの中にパスワードを含めていました。しかし、先程から申し上げてます通り、この方法ですと、ソースコードを格納しているGitHubなどのリポジトリから不意にパスワードが漏洩してしまう可能性もありますし、リポジトリに正当なアクセス権限を持つ悪意ある開発者が不正にパスワードを利用するかもしれません。
そこでAzure Key Vaultの出番です。ソースコード内に埋め込む予定であったパスワードなどの資格情報をAzure Key Vaultに格納してソースコードから除外し、Azure Key VaultにはAzure ADで適切に認証・認可されたアプリケーションだけアクセスするように権限付与を行います。これにより、従来よりもセキュアに資格情報を扱うことができます。
とはいいつつも、この説明だけでは中々にわかりにくいと思いますので、これより図を交えてわかりみ深く説明していきます。
Azure Key Vaultを使ったマネージドIDの活用
Azure Key Vaultを使ったマネージドIDの構成図は以下のようになります。下図にある「外部サービス」というのは、Azureサービス以外の外部サービスを表しています。そして、この外部サービスにアクセスするためのRest APIが提供されており、Rest APIを使うためにはシークレットが必要となるという前提でご説明してまいります。
①では、システム管理者が外部サービスにアクセスするためのAPIのシークレットをAzure Key Vaultに格納します。
②では、システム管理者がAzureポータルからマネージドIDを有効にします。
③では、①でマネージドIDを有効にした結果、Azure Resource Manager(Azureのすべてのリソースを管理するサービス)がAzure ADにサービスプリンシパルを作成します。
④では、②でサービスプリンシパルを作成したときに用いたクライアントIDと証明書をAzure Resource ManagerがAzure Instance Metadata Serviceに登録します。Azure Instance Metadata Serviceについては「マネージドIDの超ザックリな説明」の「マネージドIDがある世界」も説明しました。けれども、大事なことですので、くどいようですがもう一度説明します。
Azure Instance Metadata Serviceはアクセス元のVMを識別して、そのVM固有の情報(仮想マシン名やリージョン情報など)を返す特殊なサービスです。
Azure Instance Metadata ServiceのIPアドレスは169.
要は、あるVMの情報をAzure Instance Metadata Serviceから取得したい場合は、絶対にそのVM自身からAzure Instance Metadata Serviceにアクセスする必要があるというわけです。AというVMからAzure Instance Metadata Serviceにアクセスしても、BというVMの情報を取れることは絶対にありません。
そして、Azure Instance Metadata Serviceから返される情報には、先程④で登録したクライアントIDや証明書などの情報も含まれます。
また、「マネージドIDの超ザックリな説明」の「マネージドIDがある世界」では、Azure Instance Metadata Serviceに登録されるのはIDとパスワードと書きました。説明を簡略化するためにこのように書いたのですが、実際は先の図のようにAzure Instance Metadata Serviceに格納されるのはサービスプリンシパルにアクセスするためのクライアントIDと証明書になります。
⑤では、システム管理者がサービスプリンシパルに権限を与えます。与える権限の内容は、Azure Key Vaultに格納されているAPIのシークレットへの参照権限です。これによりこのサービスプリンシパルがAzure Key Vaultにアクセスできるようになります。つまりマネージドIDは作っただけではダメで、適切な権限を与えてあげないとダメというわけです。マネージドIDがサービスプリンシパルということを考えれば当然のことになります。
⑥では、VMの中のプログラムは、Azure Instance Metadata Serviceにアクセスします。
⑦では、Azure Instance Metadata Serviceは、自身の中に保存してある資格情報(クライアントID、証明書)を用いてAzure ADに接続します。
⑧では、⑦の結果としてトークンを受け取ります。
⑨では、Azure Instance Metadata は、Azure ADから受け取ったトークンをそのままVM内のプログラムに返します。
⑩では、先程取得したトークンでAzure Key Vaultにアクセスします。Azure Key VaultはAzureのサービスなので、Azure ADのトークンを解釈できます。
⑪では、Azure Key Vaultは、⑩で渡されたトークンが正しいかどうかを検証して、もし正しければ、外部サービスにアクセスするためのAPIのシークレットを返します。
⑫では、⑪で取得したAPIシークレットを外部サービスに渡して、外部サービスから情報を取得します。
このような流れになるわけですが、つまり外部サービスにアクセスするための資格情報(APIのシークレット)を、Azure ADのトークンを解釈できるAzure Key Vaultというサービスに格納し、マネージドIDによって得られたトークンでAzure Key VaultにアクセスをしてVMがAPIのシークレットをゲットするわけです。
つまり、先程説明した「マネージドIDがある世界」では、Azure Blob StorageがAzure ADに対応したサービスだから、直接Azure ADから取得したトークンで認証認可できるのだけど、今回の外部サービスはAzure ADに対応していないので、一旦Azure Key Vaultに外部サービスの資格情報を格納して、Azure ADから取得したトークンでAzure Key Vaultから外部サービスの資格情報を取得して外部サービスにアクセスしに行くわけです。
Azure Key Vaultが絡む分、若干複雑ですが、マネージドIDを使った認証・認可はAzure以外のサービスでも使えるということでした。
実際にやってみよう!!
では実際にAzure key VaultとマネージドIDを使ってAzure Key Vaultからパスワードを取得してみましょう。
ちなみにマネージドIDは、「マネージドIDがある世界」で実施した手順を元に、すでに作成済みであるのものとします。
まずはAzure Key Vaultを作成してみます。
Azureポータルの画面上部の検索テキストボックスに「key」と入力して表示される「キーコンテナー」をクリックしてください。
「作成」をクリックします。
「サブスクリプション」と「リソースグループ」「場所」は先程作成したVMと同じものを選びます。「Key Vault名」はキーコンテナーを一意に識別できるものなら何でもOK、他はデフォルトで問題ありません。最後に「確認および作成」をクリックします。あ、「Key Vault名」は後で使うのでメモしておきます。
「作成」をクリックします。しばらくすると出来上がります。
外部サービスのAPIにアクセスするためのシークレットを作成します。Key Vaultのリソースにアクセスして左部メニューの「シークレット」をクリックして、「生成/インポート」をクリックします。
「名前」は「apisecret」、値はデータベースのパスワードを入力します。その他の値は以下の通りに入力して、「作成」をクリックします。
ちなみに、後で必要になるのでコンテナーの URIをメモしておきます。Key Vaultのリソースにアクセスして左部メニューの「概要」をクリックして、画面右部に表示される「コンテナーのURI」をメモしておきます。
次に、Azure Key Vaultへのアクセス権限を付与します。先程申し上げましたようにマネージドIDは作っただけではなんの役にも立ちません。マネージドIDはサービスプリンシパルなので、アクセス対象のリソースに適切な権限を割り当てる必要があります。ということで、マネージドIDに権限を付与して、先程作成したシークレットにアクセス出来るようにします。
Azure Key Vaultのリソースの画面にアクセスして、左部メニューの「アクセスポリシー」→「+アクセスポリシーの追加」の順にクリックします。
「キーのアクセス許可」「シークレットのアクセス許可」両方とも「取得」のみでOKです。「プリンシパルの選択」は先程のマネージドIDの名前で検索して、表示されたものを選択します。最後に「追加」をクリックします。
「保存」をクリックしないと反映されないので忘れずに!!
これで準備は整いました。では、VMのマネージドIDを使ってAzure Key Vaultにアクセスするためのトークンを取得します。以下のコマンドを実行します。
$ curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -H Metadata:true
以下のレスポンスがあります。「access_token」の部分がAzure Key Vaultにアクセスするためのトークンになりますので、メモします。
{"access_token":"eyJ0eXAi...", "refresh_token":"", "expires_in":"3599", "expires_on":"1504130527", "not_before":"1504126627", "resource":"https://vault.azure.net", "token_type":"Bearer"}
では、このトークンを使ってAzure Key Vaultから外部サービスのAPIシークレットを取得します。以下のコマンドを実行します。
$ curl '[先程メモしたコンテナーのURI]/secrets/[先程メモしたKey Vault名]?api-version=2016-10-01' -H "Authorization: Bearer [先程メモしたトークン]"
以下のレスポンスがあります。valueのフィールドに、Azure Key Vaultに登録した外部サービスのAPIシークレットが表示されているはずです。
{"value":"password","id":"[先程メモしたコンテナーのURI]/secrets/[先程メモしたKey Vault名]/7c2204c6093c4d859bc5b9eff8f29050","attributes":{"enabled":true,"created":1505088747,"updated":1505088747,"recoveryLevel":"Purgeable"}}
いかがでしょうか?マネージドIDとAzure Key Vaultを使って外部サービスの資格情報もソースコードから追い出して、安全に管理できることがご理解いただけたかと思います。
まとめ
昨今はGitHubなどのリポジトリからパスワードが漏洩したりと、資格情報のセキュアな管理はその需要がますます増しています。そんな情勢の中、マネージドIDはもはや必須のサービスと言えます。今までなんとなくご利用になっていたマネージドIDも、本記事でその本質をご理解頂ければ幸いかと思います。