Azure API Management를 사용하여 GeoCatalog에 대한 API 프록시 만들기

이 문서에서는 Microsoft Planetary Computer Pro GeoCatalog 앞에서 APIM( Azure API Management )을 API 프록시로 설정하는 방법을 안내합니다. 이 구성을 사용하면 다음을 수행할 수 있습니다.

  • 익명 액세스 사용: 호출자는 자신의 Microsoft Entra 자격 증명이 필요하지 않습니다. APIM은 관리 ID를 사용하여 GeoCatalog를 대신하여 인증합니다.
  • 비 Entra 인증: 호출자는 비 Entra 기반 인증 방법을 지원할 수 있습니다. APIM은 관리 ID를 사용하여 GeoCatalog를 대신하여 인증합니다.
  • 컬렉션 수준 액세스 제어 적용: GeoCatalogs가 기본적으로 RBAC(컬렉션 수준 역할 기반 액세스 제어)를 지원하지 않더라도 프록시를 통해 표시되는 STAC(Spatiotemporal Access Catalog) 컬렉션을 제한합니다.

다음 다이어그램에서는 APIM 프록시를 추가하기 전후의 아키텍처를 보여 줍니다.

전에 모든 호출자는 GeoCatalog에 직접 인증합니다.

caller ──(Entra token)──► GeoCatalog

후 APIM은 호출자와 GeoCatalog 사이에 위치하여 인증 및 액세스 제어를 처리합니다.

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

사전 요구 사항

APIM에 관리 ID 할당

APIM이 GeoCatalog에 인증하려면 APIM 인스턴스와 사용자 할당 관리 ID를 연결해야 합니다.

  1. Azure Portal에서 API Management 인스턴스로 이동합니다.
  2. 왼쪽 사이드바에서 ID 를 선택합니다.
  3. 사용자 할당 탭을 선택합니다.
  4. 추가를 선택한 다음 GeoCatalog에서 GeoCatalog 판독기 역할이 있는 사용자 할당 관리 ID를 선택합니다.
  5. 추가를 선택하여 확인합니다.

APIM에서 API 만들기

GeoCatalog 백 엔드에 요청을 프록시하는 APIM에서 새 API를 정의합니다.

  1. APIM 인스턴스의 왼쪽 사이드바에서 API 를 선택합니다.

  2. + API>HTTP 추가를 선택합니다.

  3. 다음 설정을 사용하여 API를 구성합니다.

    설정 Value
    표시 이름 설명이 포함된 이름(예: GeoCatalog API)입니다.
    웹 서비스 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
단일 컬렉션 가져오기 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 Value
<managed-identity-client-id> APIM에 할당된 사용자 할당 관리 ID의 클라이언트 ID
<name>.<id>.<region> GeoCatalog 엔드포인트의 구성 요소들
<apim-name> APIM 인스턴스의 이름

다음 표에서는 각 정책 요소에 대해 설명합니다.

Policy 요소 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 링크(self, root, parent등)는 백 엔드 URL을 호출자에게 노출합니다.

Accept-Encoding 헤더를 제거하는 이유

Python requests 및 httpx 와 같은 클라이언트는 기본적으로 Accept-Encoding: gzip, deflate를 전송합니다. 백 엔드가 이 헤더를 받으면 압축된 응답을 반환합니다. 원시 응답 본문에서 작동하고 압축을 해제할 수 없는 것과 같은 find-and-replace APIM 아웃바운드 정책은 자동으로 아무 작업도 수행하지 않습니다. 헤더를 제거하면 백 엔드가 아웃바운드 정책이 처리할 수 있는 압축되지 않은 응답을 반환합니다.

메모

curl와 wget는 기본적으로 Accept-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 엔드포인트는 매개 변수를 collections GET 또는 POST의 JSON 본문에 쿼리 문자열로 허용합니다. 가드레일이 없으면 호출자가 카탈로그의 모든 컬렉션에서 검색할 수 있습니다. 다음 정책은 허용된 집합의 컬렉션만 요청되는지 확인합니다.

다음 두 가지 작업을 추가합니다.

표시 이름 메서드 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>

메모

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 토큰 경로에 허용되는 컬렉션 적용

GeoCatalog SAS API를 사용하면 호출자가 스토리지 토큰을 생성하고 자산 HREF에 서명할 수 있습니다. 제한 없이 호출자는 컬렉션에 대한 토큰을 가져올 수 있습니다. 다음 정책은 허용되는 컬렉션만 액세스할 수 있도록 합니다.

다음 작업을 추가합니다.

표시 이름 메서드 URL 템플릿
SAS 토큰 가져오기 GET /sas/token/{collection_id}
SAS 기호 차단 GET /sas/sign

404 차단 정책(루트 및 컬렉션 블록과 동일)을 SAS 차단 서명 작업에 적용합니다. 호출자는 대신 컬렉션 수준의 SAS 토큰을 얻기 위해 /sas/token/{collection_id}를 사용해야 합니다.

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. 등록 검색 - 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>

자주 묻는 질문

허용된 컬렉션 목록을 업데이트하려면 어떻게 해야 하나요?

allowed-collections APIM 인스턴스에서 명명된 값을 편집합니다. 정책 변경이 필요하지 않습니다.

호출자가 컬렉션 매개 변수를 생략하면 어떻게 되나요?

요청이 거부 403 Forbidden되었습니다. 호출자는 항상 검색할 컬렉션을 지정해야 합니다.