Azure API Management を使用して GeoCatalog の API プロキシを作成する

この記事では、Microsoft プラネタリー コンピューター Pro GeoCatalog の前に API プロキシとして Azure API Management (APIM) を設定する方法について説明します。 この構成では、次のことができます。

  • 匿名アクセスを有効にする: 呼び出し元は、独自の Microsoft Entra 資格情報を必要としません。 APIM は、マネージド ID を使用して、GeoCatalog に代わって認証を行います。
  • 非 Entra 認証: 呼び出し元は、非 Entra ベースの認証方法をサポートできます。 APIM は、マネージド ID を使用して、GeoCatalog に代わって認証します。
  • コレクション レベルのアクセス制御を適用する: GeoCatalogs がコレクション レベルのロールベースのアクセス制御 (RBAC) をネイティブにサポートしていない場合でも、プロキシ経由で表示される Spatiotemporal Access Catalog (STAC) コレクションを制限します。

次の図は、APIM プロキシを追加する前と後のアーキテクチャを示しています。

以前は すべての呼び出し元は、GeoCatalog に対して直接認証を行います。

caller ──(Entra token)──► GeoCatalog

APIM は呼び出し元と GeoCatalog の間にあり、認証とアクセス制御を処理します。

caller ──(anonymous / APIM Subscription Keys)──► APIM ──(managed identity token)──► GeoCatalog

前提条件

APIM にマネージド ID を割り当てる

APIM が GeoCatalog に対して認証を行う前に、ユーザー割り当てマネージド ID を APIM インスタンスに関連付ける必要があります。

  1. Azure ポータルで、API Management インスタンスに移動します。
  2. 左側のサイドバーから [ID] を 選択します。
  3. [ユーザー割り当て済み] タブを選択します。
  4. [ 追加] を選択し、 GeoCatalog の GeoCatalog 閲覧者 ロールを持つユーザー割り当てマネージド ID を選択します。
  5. [ 追加] を選択して確定します。

APIM で API を作成する

GeoCatalog バックエンドに要求をプロキシする APIM で新しい API を定義します。

  1. APIM インスタンスで、左側のサイドバーから API を 選択します。

  2. + API の追加>HTTPを選択します。

  3. 次の設定で API を構成します。

    Setting 価値
    表示名 わかりやすい名前 (例: GeoCatalog API)
    Web サービスの URL GeoCatalog エンドポイント ( https://<name>.<id>.<region>.geocatalog.spatio.azure.com など)
    URL スキーム HTTPS
    API URL サフィックス 空のままにする (ルート パス)
    サブスクリプションが必要 いいえ。匿名アクセスの場合。 はい(サブスクリプション キー ベースのアクセスの場合)
  4. を選択してを作成します。

API 操作の定義

GeoCatalog API サーフェスに一致するように、次の操作を追加します。 ワイルドカード (/*) 操作は、一致するすべての要求をバックエンドに転送します。 明示的なコレクション操作を使用すると、後でアクセス制御のためにコレクション固有のポリシーを適用できます。

表示名 メソッド URL テンプレート
GET GET /*
コレクション項目を取得する GET /stac/collections/{collection_id}/items
1 つのコレクションを取得する GET /stac/collections/{collection_id}
コレクション サブリソースを取得する GET /stac/collections/{collection_id}/*
投稿 投稿 /*

各操作を追加するには:

  1. 作成した API を選択します。
  2. [+ 操作の追加] を選択します。
  3. 前の表の 表示名メソッドおよび URL テンプレート を入力します。
  4. 保存を選びます。

API レベルのポリシーを構成する

API レベルのポリシーは、API 全体の認証と URL の書き換えを処理します。 このポリシーは、ユーザー割り当てマネージド ID からトークンを取得し、GeoCatalog バックエンドに転送されるすべての要求にトークンをアタッチします。

  1. 作成した API を選択し、[ すべての操作] を選択します。
  2. [受信処理] セクションで、</> (コード エディター) アイコンを選択します。
  3. ポリシーの内容を次のポリシーに置き換えます。
<policies>
    <inbound>
        <base />
        <authentication-managed-identity
            resource="https://geocatalog.spatio.azure.com"
            client-id="<managed-identity-client-id>" />
        <set-header name="Accept-Encoding"
            exists-action="delete" />
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
        <find-and-replace
            from="https://<name>.<id>.<region>.geocatalog.spatio.azure.com"
            to="https://<apim-name>.azure-api.net" />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

次のプレースホルダーを置き換えてください。

Placeholder 価値
<managed-identity-client-id> APIM に割り当てられているユーザー指定のマネージド ID のクライアント ID
<name>.<id>.<region> GeoCatalog エンドポイント コンポーネント
<apim-name> APIM インスタンスの名前

次の表では、各ポリシー要素について説明します。

ポリシー要素 Purpose
authentication-managed-identity 指定したマネージド ID を使用して https://geocatalog.spatio.azure.com 対象ユーザーのトークンを取得し、送信要求にアタッチします。
set-header ( Accept-Encodingの削除) 受信要求から Accept-Encoding ヘッダーを削除します。 「 Accept-Encoding」を削除する理由を参照してください。
find-and-replace 応答本文の GeoCatalog バックエンド URL を APIM ゲートウェイ URL に書き換えます。 この書き換えを行わないと、STAC リンク (selfrootparentなど) によって、バックエンド URL が呼び出し元に公開されます。

なぜ Accept-Encoding を取り除くのか

Python requestshttpx などのクライアントは、既定で Accept-Encoding: gzip, deflate を送信します。 バックエンドがこのヘッダーを受信すると、圧縮された応答が返されます。 find-and-replaceなどの APIM 送信ポリシーは、生の応答本文で動作し、展開できないため、サイレントモードで何も実行しません。 ヘッダーを削除すると、送信ポリシーで処理できる非圧縮応答がバックエンドに強制的に返されます。

Note

curl 既定では、 wgetAccept-Encoding を送信しません。 つまり、これらのツールを使用してテストすると、送信ポリシーが正しく機能するように見えます。 不整合は、圧縮を要求するクライアントでのみ発生します。

コレクション レベルのアクセス制御を適用する

既定では、GeoCatalog はすべてのコレクションを認証された呼び出し元に公開します。 APIM を通じて表示されるコレクションを制限するには、広範な STAC 検出をブロックし、許可リストを適用する操作レベルのポリシーを適用します。

許可されるコレクションを定義する

APIM に名前付き値を作成して、許可されたコレクション ID の一覧を格納します。

  1. APIM インスタンスで、左側のサイドバーから [名前付き値 ] を選択します。
  2. [+ 追加] を選択します。
  3. [名前] を [allowed-collectionsに設定します。
  4. [値] を、許可されるコレクション ID のコンマ区切りのリスト (たとえば、sentinel-2-l2a,landsat-8-c2-l2) に設定します。
  5. 保存を選びます。

ランディング ページとコレクションの一覧をブロックする

カタログ内のすべてのコレクションを表示するルートをブロックします。 次の操作を追加し、すぐに 404 を返すポリシーをアタッチします。

表示名 メソッド URL テンプレート
ルートをブロックする GET /
コレクションをブロックする GET /stac/collections

両方の操作に次の操作レベルのポリシーを適用します。

<policies>
    <inbound>
        <base />
        <return-response>
            <set-status code="404" reason="Not Found" />
        </return-response>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

STAC /stac/search エンドポイントは、GET のクエリ文字列として、または POST の JSON 本文で、 collections パラメーターを受け取ります。 ガードレールがないと、呼び出し元はカタログ内のすべてのコレクションを検索できます。 次のポリシーは、許可されたセットからのコレクションのみが要求されることを検証します。

次の 2 つの操作を追加します。

表示名 メソッド URL テンプレート
GET 検索 GET /stac/search
POST 検索 投稿 /stac/search

GET /stac/search policy

このポリシーは、 collections クエリ パラメーターを検証します。 コンマ区切りの各値は、許可されるセットに含まれている必要があります。 collections パラメーターのない要求は、403 Forbiddenで拒否されます。

GET 検索操作に次のポリシーを適用します。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var raw = context.Request.Url.Query
                    .GetValueOrDefault("collections", "");
                if (string.IsNullOrWhiteSpace(raw)) {
                    return true;
                }
                foreach (var c in raw.ToLower().Split(
                    new [] { "," },
                    StringSplitOptions.RemoveEmptyEntries))
                {
                    if (!c.Trim().Equals(allowed)) {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

Note

APIM ポリシー式は、制限された C# 環境で実行されます。 式内で二重引用符が機能するように、 condition='@{...}' (一重引用符で囲まれた属性) を使用します。 ジェネリック型パラメーター ( GetValueOrDefault<string> など) と LINQ ラムダは使用しないでください。代わりに、明示的なキャストと foreach ループを使用します。

POST /stac/search policy

このポリシーは、JSON 本文を解析し、 collections 配列を検証します。 collections パラメーターのない要求は、403 Forbiddenで拒否されます。

POST 検索操作に次のポリシーを適用します。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <set-variable name="requestBody"
            value="@(context.Request.Body
                .As&lt;string&gt;(
                    preserveContent: true))" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var body = (string)context
                    .Variables["requestBody"];
                var json = Newtonsoft.Json.Linq
                    .JObject.Parse(body);
                var arr = json["collections"]
                    as Newtonsoft.Json.Linq.JArray;
                if (arr == null || arr.Count == 0) {
                    return true;
                }
                foreach (var token in arr) {
                    if (!token.ToString().Trim()
                        .ToLower().Equals(allowed))
                    {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

コレクション エンドポイントに対して、許可されるコレクションを適用する

明示的な操作がない場合、 GET /stac/collections/sentinel-2-l2aGET /stac/collections/sentinel-2-l2a/items などの要求は GET /* ワイルドカードに到達し、コレクション レベルのチェックなしでバックエンドに到達します。 collection_idで作成した次の操作に、{{allowed-collections}}に対してを検証するパス パラメーター ポリシーを適用します。

表示名 メソッド URL テンプレート
1 つのコレクションを取得する GET /stac/collections/{collection_id}
コレクション サブリソースを取得する GET /stac/collections/{collection_id}/*
コレクション項目を取得する GET /stac/collections/{collection_id}/items

3 つの操作すべてに次のポリシーを適用します。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collection_id"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

SAS トークン ルートに許可されたコレクションを適用する

GeoCatalog SAS API を使用すると、呼び出し元はストレージ トークンを生成し、資産 HREF に署名できます。 制限なしに、呼び出し元は任意のコレクションのトークンを取得できます。 次のポリシーを使用すると、許可されたコレクションにのみアクセスできます。

次の操作を追加します。

表示名 メソッド URL テンプレート
GET SAS トークン GET /sas/token/{collection_id}
SAS 署名をブロックする GET /sas/sign

404ブロック ポリシー (ルートおよびコレクション ブロックと同じ) をブロック SAS署名操作に適用します。 呼び出し元は、代わりに /sas/token/{collection_id} を使用してコレクション レベルの SAS トークンを取得する必要があります。

GET SAS トークン操作に次のポリシーを適用します。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collection_id"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

データルートに許可されたコレクションを強制する

/data/mosaic/ エンドポイントは、タイルレンダリング、境界ボックストリミング、および検索登録を提供します。 次の 2 つのポリシー グループが必要です。

  1. レジスター検索 - JSON 本文内の collections 配列を検証します。
  2. その他のすべてのコレクション ルート - collectionId パス パラメーターを検証します。

次の操作を追加します。

表示名 メソッド URL テンプレート
POST レジスタ検索 投稿 /data/mosaic/register
GET データ収集 GET /data/mosaic/collections/{collectionId}/*

POST /data/mosaic/register ポリシー

このポリシーは、許可されたセットに対して JSON 本文の collections 配列を検証します。 collections パラメーターのない要求は拒否されます。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <set-variable name="requestBody"
            value="@(context.Request.Body
                .As&lt;string&gt;(
                    preserveContent: true))" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var body = (string)context
                    .Variables["requestBody"];
                var json = Newtonsoft.Json.Linq
                    .JObject.Parse(body);
                var arr = json["collections"]
                    as Newtonsoft.Json.Linq.JArray;
                if (arr == null || arr.Count == 0) {
                    return true;
                }
                foreach (var token in arr) {
                    if (!token.ToString().Trim()
                        .ToLower().Equals(allowed))
                    {
                        return true;
                    }
                }
                return false;
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

GET /data/mosaic/collections/{collectionId}/* ポリシー

このポリシーは、許可されたセットに対して collectionId パス パラメーターを検証します。 このポリシーを両方の GET データ収集 操作に適用します。

<policies>
    <inbound>
        <base />
        <set-variable name="allowedCsv"
            value="{{allowed-collections}}" />
        <choose>
            <when condition='@{
                var allowed = ((string)context
                    .Variables["allowedCsv"])
                    .Trim().ToLower();
                var collectionId = (string)context
                    .Request.MatchedParameters[
                        "collectionId"];
                return !collectionId.Trim()
                    .ToLower().Equals(allowed);
            }'>
                <return-response>
                    <set-status code="403"
                        reason="Forbidden" />
                    <set-body>
                        Collection not allowed.
                    </set-body>
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base />
    </backend>
    <outbound>
        <base />
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

よく寄せられる質問

許可されているコレクションの一覧を更新するにはどうすればよいですか?

APIM インスタンスの allowed-collections 名前付き値を編集します。 ポリシーの変更は必要ありません。

呼び出し元が collections パラメーターを省略した場合はどうなりますか?

要求は 403 Forbiddenで拒否されます。 呼び出し元は、検索するコレクションを常に指定する必要があります。