このエントリは2022/05/16現在の情報に基づいています。将来の機能追加や変更により、記載内容との乖離が発生する場合があります。
例によって以下のような問い合わせがあった。
Key Vaultにシークレットを配置しているのだが、それを取り出すためのAPIを作成したい。Key VaultのREST APIでもよいのだが、認証が少々面倒なので、API Managementを挟んでもう少し簡単に呼び出せるようにしたいのだが、どうすればよいか?
そもそもシークレット値を返すAPIを作ってどうなわけ?と思うところではあるが、ちょっとしたAzure API Managementの練習と思って作成してみる。やり方は大きく分けて2種類。
- Key VaultのREST APIをバックエンドサービスとして利用するAPIを作成する
- Named ValuesとKey Vaultを統合して、シークレット値を取り出す
前者は、いわゆるFaçadeとしてAPI Managementを配置する方法、後者はAPI Management内の統合機能を使う方法。
1. Key Vault REST APIを使う
こちらはイメージしやすいかもしれない。構成にあたってのポイントは、Managed Identityを使った認証と、バックエンドサービス(Key Vault REST API)からのレスポンスに手を入れる点。
Managed Identityを使った認証は、以下のエントリにも記載の通りで、Authenticate Managed Identityポリシーを使う。
Key Vaultの場合は以下のドキュメントにある通り、 https://vault.azure.net をリソースとして指定する。
マネージド ID を使用してバックエンド サービスで認証する / Use managed identity to authenticate with a backend service
https://docs.microsoft.com/azure/api-management/api-management-authentication-policies#use-managed-identity-to-authenticate-with-a-backend-service
Key Vault REST APIからのレスポンスは以下のよう。一見してわかる通り、Key VaultのURLなどが含まれているため、適切にマスクしたり、余計な要素を削除するなどの編集が必要である。
{
"value": "Hello world!",
"id": "https://<Key Vault instance name>.vault.azure.net/secrets/test1/2bd097a2861f4422b4f8ace036f41039",
"attributes": {
"enabled": true,
"created": 1652407151,
"updated": 1652407151,
"recoveryLevel": "Recoverable+Purgeable",
"recoverableDays": 90
},
"tags": {}
}
例えばid、attributes、tagsといった要素が不要なら、ポリシー式で削除しておく。例えば以下のよう。
- 【Inboundパイプライン】
- Key Vault REST APIの呼び出しに必要なapi-versionをクエリパラメータとして付加するため、rewrite-uriポリシーを使って変更している
- 【Outboundパイプライン】
- 削除対象の要素 (attributes、id、tags) があれば削除する
<policies>
<inbound>
<base />
<authentication-managed-identity resource="https://vault.azure.net" />
<rewrite-uri template="/{key}?api-version=7.3" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-body>@{
var response = context.Response.Body.As<JObject>();
foreach (var key in new [] {"attributes", "id", "tags"}) {
response.Property (key).Remove ();
}
return response.ToString();
}</set-body>
</outbound>
<on-error>
<base />
</on-error>
</policies>
このポリシーを設定すると、以下のような結果を取得できるAPIができあがる。
{
"value": "Hello world!"
}
2. Named Valuesを使う
Key VaultシークレットはNamed values (名前付き値) と統合できるようになっている。
Azure API Management ポリシーで名前付きの値を使用する / Use named values in Azure API Management policies
https://docs.microsoft.com/azure/api-management/api-management-howto-properties
名前付き値を拾い出して、その結果を返す形。注意点は、
- バックエンドサービスではないので、Inboundパイプラインでレスポンスを返す必要がある
{{}}
で名前付き値を取り出すことができるものの、この{{}}
の中にコンテキスト変数を指定できない(指定したとしても、その指定したポリシー式が表示されるのみ)- そのため、現時点ではリテラルで指定するしかない。
というわけで、例えばkeyというPath parameterと一致する名前付き値を引き出すこと自体は可能だが、直接指定しているだけなので、あまり汎用性が高くはない(それに一致しない場合に404を返すこと自体どうなの、というのはその通り)。
<policies>
<inbound>
<base />
<choose>
<when condition="@(context.Request.MatchedParameters["key"].ToString().Equals("test1"))">
<return-response>
<set-status code="200" reason="Successful" />
<set-body>{{test1}}</set-body>
</return-response>
</when>
<otherwise>
<return-response>
<set-status code="404" reason="Not found" />
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
で、この両者を伝えたところ、「汎用性を上げるなら1.のやり方かなー」と、あっさりと受け入れてくれた。もちろん認証やアクセス元の制限を加えることも申し入れしておいた。