Создание прокси-сервера API для GeoCatalog с помощью службы "Управление API Azure"

В этой статье объясняется, как настроить Azure API Management (APIM) в качестве прокси-сервера для API перед Microsoft Planetary Computer Pro GeoCatalog. С помощью этой конфигурации можно:

  • Включить анонимный доступ: вызывающие не нуждаются в собственных учетных данных Microsoft Entra. APIM проходит проверку подлинности в GeoCatalog от их имени с помощью управляемого удостоверения.
  • Аутентификация без использования Entra: вызывающие пользователи могут поддерживать методы аутентификации, отличные от основанных на Entra. APIM проходит проверку подлинности в GeoCatalog от их имени с помощью управляемого удостоверения.
  • Принудительное управление доступом на уровне коллекции: ограничение того, какие коллекции в Spatiotemporal Access Catalog (STAC) отображаются через прокси, даже если GeoCatalogs не поддерживают управление доступом на основе ролей (RBAC) на уровне коллекции.

На следующей схеме показана архитектура до и после добавления прокси-сервера APIM:

Перед тем как каждый вызывающий абонент проходит проверку подлинности непосредственно в GeoCatalog:

caller ──(Entra token)──► GeoCatalog

После APIM находится между вызывающими абонентами и GeoCatalog, обрабатывая проверку подлинности и управление доступом:

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

Необходимые условия

Назначьте управляемое удостоверение для APIM

Прежде чем APIM сможет пройти проверку подлинности в GeoCatalog, необходимо связать управляемое удостоверение, назначаемое пользователем, с экземпляром APIM.

  1. Перейдите на портал Azure и откройте ваш экземпляр диспетчера API.
  2. Выберите Идентификатор в левой боковой панели.
  3. Выберите вкладку Назначено пользователем.
  4. Выберите Добавить, а затем выберите управляемое удостоверение, назначенное пользователем, с ролью GeoCatalog Reader в вашем GeoCatalog.
  5. Нажмите кнопку "Добавить ", чтобы подтвердить.

Создание API в APIM

Определите новый API в APIM, который проксирует запросы к вашей серверной части GeoCatalog.

  1. В экземпляре APIM выберите API на левой боковой панели.

  2. Выберите и добавьте HTTP API>.

  3. Настройте API со следующими параметрами:

    Setting Ценность
    Отображаемое имя Описательное имя (например, GeoCatalog API)
    URL-адрес веб-службы Конечная точка GeoCatalog (например, https://<name>.<id>.<region>.geocatalog.spatio.azure.com)
    Схема URL-адресов HTTPS
    Суффикс URL-адреса API Оставьте пустым (корневой путь)
    Требуется подписка Нет, для анонимного доступа; Да, для доступа на основе ключа подписки
  4. Нажмите кнопку "Создать".

Определение операций API

Добавьте следующие операции для сопоставления поверхности API GeoCatalog. Операции с подстановочными знаками (/*) перенаправляют все соответствующие запросы в серверную часть. Явные операции сбора позволяют применять политики, относящиеся к коллекции, позже для управления доступом.

Показать имя Метод Шаблон URL-адреса
GET GET /*
Получение элементов коллекции GET /stac/collections/{collection_id}/items
Получить одну коллекцию GET /stac/collections/{collection_id}
Получение подресурсов коллекции GET /stac/collections/{collection_id}/*
ПОСТ ПОСТ /*

Чтобы добавить каждую операцию, выполните приведенные действия.

  1. Выберите созданный API.
  2. Выберите и добавьте операцию.
  3. Введите отображаемое имя, метод и ШАБЛОН URL-адреса из предыдущей таблицы.
  4. Нажмите Сохранить.

Настройка политики уровня API

Политика уровня API обрабатывает проверку подлинности и перезапись URL-адресов для всего API. Эта политика получает токен из управляемого удостоверения, назначаемого пользователем, и присоединяет его к каждому запросу, пересылаемому в бэкенд 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>

Замените следующие заполнители:

Заполнитель Ценность
<managed-identity-client-id> Идентификатор клиента управляемой идентичности, назначенной пользователем в APIM
<name>.<id>.<region> Компоненты конечной точки GeoCatalog
<apim-name> Имя экземпляра APIM

В следующей таблице описан каждый элемент политики:

Элемент политики Purpose
authentication-managed-identity Получает токен для https://geocatalog.spatio.azure.com аудитории с помощью заданного управляемого удостоверения и добавляет его к исходящему запросу.
set-header (удаление Accept-Encoding) Удаляет Accept-Encoding заголовок из входящих запросов. См. Почему следует убрать Accept-Encoding.
find-and-replace Заменяет URL-адрес бэкенда GeoCatalog в телах ответов на URL-адрес шлюза APIM. Без этой перезаписи ссылки STAC (self, root, parent и так далее) раскрывают внутренний URL-адрес вызывающим.

Почему удалить Accept-Encoding

Клиенты, такие как Python requests и httpx, по умолчанию отправляют Accept-Encoding: gzip, deflate. Когда серверная часть получает этот заголовок, она возвращает сжатый ответ. Политики исходящих соединений APIM, такие как find-and-replace, работают с необработанным телом ответа и не могут его распаковать, из-за чего остаются неэффективными. Удаление заголовка заставляет серверную часть возвращать несжатый ответ, который могут обрабатывать политики для обработки исходящего трафика.

Note

curl и wget не отправляйте Accept-Encoding по умолчанию. Это означает, что политики исходящего трафика работают правильно при тестировании с помощью этих средств. Несоответствие применяется только к клиентам, запрашивающим сжатие.

Обеспечение управления доступом на уровне коллекции

По умолчанию GeoCatalog предоставляет все свои коллекции любому аутентифицированному пользователю. Чтобы ограничить, какие коллекции отображаются через APIM, примените политики уровня операций, которые блокируют широкое обнаружение STAC и применяют список разрешений.

Определение разрешенных коллекций

Создайте именованное значение в APIM для хранения списка разрешенных идентификаторов коллекции:

  1. В вашем экземпляре APIM выберите именованные значения в левом боковом меню.
  2. Щелкните + Добавить.
  3. Установите Имя на allowed-collections.
  4. Установите Значение как список, разделённый запятыми, из разрешённых идентификаторов коллекции (например, 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 принимает collections параметр в виде строки запроса в GET или в тексте JSON в POST. Без ограничений вызывающий может выполнять поиск по каждой коллекции в каталоге. Следующие политики проверяют соответствие, чтобы запрашивались только коллекции из разрешенного множества.

Добавьте две операции:

Показать имя Метод Шаблон URL-адреса
ПОИСК GET GET /stac/search
Поиск POST ПОСТ /stac/search

GET /stac/search правила использования

Эта политика проверяет 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

Эта политика анализирует текст 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-l2a или GET /stac/collections/sentinel-2-l2a/items попадают в GET /* подстановочный знак и достигают серверной части без каких-либо проверок на уровне коллекции. Примените политику пути-параметра, которая проверяет collection_id против {{allowed-collections}}, к следующим операциям, созданным в разделе Определение операций API:

Показать имя Метод Шаблон URL-адреса
Получить одну коллекцию GET /stac/collections/{collection_id}
Получение подресурсов коллекции GET /stac/collections/{collection_id}/*
Получение элементов коллекции GET /stac/collections/{collection_id}/items

Примените следующую политику ко всем трем операциям:

<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

API SAS GeoCatalog позволяет клиентам создавать токены хранилища и подписывать HREF-адреса ресурсов. Без ограничений вызывающий объект может получить токены для любой коллекции. Следующие политики обеспечивают доступ только к разрешенным коллекциям.

Добавьте следующие операции:

Показать имя Метод Шаблон URL-адреса
Получить SAS токен GET /sas/token/{collection_id}
Блокировка сигнала SAS GET /sas/sign

Примените политику блокировки (так же, как и блокировку корня и коллекций) к операции блокировки 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/ точки предоставляют рендеринг тайлов, обрезку по ограничивающим рамкам и регистрацию поиска. Требуются две группы политик:

  1. Поиск по реестру — проверка collections массива в JSON-теле.
  2. Все остальные маршруты коллекции — проверьте collectionId параметр пути.

Добавьте следующие операции:

Показать имя Метод Шаблон URL-адреса
Поиск по регистру POST ПОСТ /data/mosaic/register
Сбор данных GET GET /data/mosaic/collections/{collectionId}/*

POST /data/mosaic/register политика

Эта политика валидирует collections массив в теле JSON на соответствие допустимому набору. Запросы без 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>

Часто задаваемые вопросы

Как обновить список разрешенных коллекций?

Измените именованное allowed-collections значение в экземпляре APIM. Изменения политики не требуются.

Что произойдет, если вызывающий объект не указывает параметр коллекций?

Запрос отклоняется.403 Forbidden Вызывающие пользователи всегда должны указывать, какие коллекции они хотят искать.