Azure API ManagementのサブスクリプションキーをRequest本文で指定したい

このエントリは2023/12/10現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。

問い合わせ

昨日の質問をして来た人からの更問(更更問、とも言える)。

Azure API Management (以下、APIM) のサブスクリプションキーはQuery ParameterもしくはHTTP Headerを使うことになっているが、Request本文に指定した値を使うことはできないか?

仕様上はQuery ParameterもしくはRequest Headerに設定しなければならず、端的に答えると「そんなことはできない」でおしまいなのだが、多段構成であれば実現可能な場合がある。

ユースケース

例えば、Request本文に

{
  "subscription-key":"xxxxxx"
}

というメッセージが入っていたらAPIのアクセスを許可する、という具合。

方法

API ManagementでホストするAPIを2段(API-1、API-2)にする。API-1をフロントエンドとし、通常のAPI利用者はAPI-1を呼び出す。処理の実体はAPI-2で実施する。いずれも同一のAPIMインスタンスにホストできるので、2段にするからといって、APIMインスタンスを複数用意する必要はない。

APIについてサブスクリプションバックエンドサービス
API-1Request本文に含まれるsubscription-keyをチェック。
含まれていれば、バックエンドサービスとして設定する後段のAPIに対して、Header(もしくはQuery Parameter)でサブスクリプションキーを渡すよう構成する。
含まれていない場合は403を返す。
構成しないAPI-2
API-2本来の処理を提供するAPI構成するN/A

図示すると以下のようなイメージ。

実装

前段のAPI (API-1) では、以下のようなポリシーを構成しておく。

  • InboundポリシーでキーがRequest本文に含まれているかを確認
    • あれば値を取得してHTTP Headerに指定する

なお、API-1へのRequest本文はこの後使わないので、2回目のcontext.Request.Body.As<T>の呼び出しではpreserveContent=trueを指定していない。

以下のポリシーは厳密なエラー処理(JSON Schemaに対するValidationなど)は含めていないので、本運用時には適切なロジックが必要。

<policies>
    <inbound>
        <base />
        <set-variable name="bExist" value="@(context.Request.Body.As<JObject>(preserveContent: true).ContainsKey("subscription-key"))" />
        <choose>
            <when condition="@((bool)context.Variables["bExist"])">
                <set-header name="Ocp-Apim-Subscription-Key" exists-action="override">
                    <value>@{
                        JObject inBody = context.Request.Body.As<JObject>();
                        return inBody["subscription-key"].ToString();
                    }</value>
                </set-header>
                <set-method>GET</set-method>
            </when>
            <otherwise>
                <return-response>
                    <set-status code="403" />
                    <set-header name="content-type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@{
                        return new JObject(
                            new JProperty("error", "No subscription key is found.")           
                        ).ToString();
                    }</set-body>
                </return-response>
            </otherwise>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

注意

多段にするとAPIの障害分析時に見通しが悪くなる可能性があるので注意が必要。また、内部モードでAPIMをデプロイしていて、HTTP 500 BackendConnectionFailure エラーが返る場合には、以下のブログエントリにあるような対応が必要になる場合がある。

Self-Chained APIM request limitation in internal Virtual network mode (Developer and Premium tier)
https://techcommunity.microsoft.com/t5/azure-paas-blog/self-chained-apim-request-limitation-in-internal-virtual-network/ba-p/1940417

この制約は内部ロードバランサー配下にあるノードから当該内部ロードバランサーを呼び出すことによるものと同様なので、プロキシを挟む(Application Gatewayを挟んでもいい)ことで回避できる。

参加しているロード バランサー バックエンド プール VM から内部 ロード バランサー フロントエンドにアクセスしている / Access of the internal load balancer frontend from the participating load balancer backend pool VM
https://learn.microsoft.com/azure/load-balancer/load-balancer-troubleshoot-backend-traffic#cause-4-access-of-the-internal-load-balancer-frontend-from-the-participating-load-balancer-backend-pool-vm

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください