Crie um proxy de API para um GeoCatálogo usando Azure API Management

Este artigo orienta-o na configuração do Azure API Management (APIM) como um proxy de API à frente de um Microsoft Planetary Computer Pro GeoCatalog. Com esta configuração pode:

  • Ative o acesso anónimo: os chamadores não precisam das suas próprias credenciais Microsoft Entra. O APIM autentica-se no GeoCatálogo em seu nome usando uma identidade gerida.
  • Autenticação não Entra: os chamadores podem suportar métodos de autenticação não baseados em Entra. O APIM autentica-se ao GeoCatalog em seu nome usando uma identidade gerida.
  • Impor controlo de acesso ao nível da coleção: restringir quais as coleções do Catálogo de Acesso Espaçotemporal (STAC) que são visíveis através do proxy, mesmo que os GeoCatálogos não suportem nativamente o controlo de acesso baseado em funções ao nível da coleção (RBAC).

O diagrama seguinte ilustra a arquitetura antes e depois de adicionar o proxy APIM:

Antes Cada chamador autentica-se diretamente no GeoCatálogo:

caller ──(Entra token)──► GeoCatalog

Após O APIM situa-se entre os chamadores e o GeoCatálogo, tratando de autenticação e controlo de acessos:

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

Pré-requisitos

Atribuir a identidade gerida ao APIM

Antes de o APIM poder autenticar-se ao seu GeoCatálogo, precisa de associar a identidade gerida atribuída pelo utilizador à instância do APIM.

  1. No portal do Azure, navegue até sua instância de Gerenciamento de API.
  2. Selecione Identidade na barra lateral esquerda.
  3. Selecione a guia Usuário atribuído .
  4. Selecione Adicionar, depois escolha a identidade gerida atribuída pelo utilizador que tenha o papel de Leitor GeoCatalog no seu GeoCatalog.
  5. Selecione Adicionar para confirmar.

Crie a API no serviço APIM

Defina uma nova API no APIM que proxie os pedidos para o seu backend GeoCatalog.

  1. Na sua instância APIM, selecione APIs na barra lateral esquerda.

  2. Selecionar + Adicionar API>HTTP.

  3. Configure a API com as seguintes definições:

    Configuração Value
    Nome de exibição Um nome descritivo (por exemplo, GeoCatalog API)
    URL do serviço Web O seu endpoint do GeoCatalog (por exemplo, https://<name>.<id>.<region>.geocatalog.spatio.azure.com)
    Esquema do URL HTTPS
    Sufixo do URL da API Deixar vazio (caminho raiz)
    Assinatura necessária Não, para acesso anónimo; Sim, para acesso baseado em Chave de Subscrição
  4. Selecione Criar.

Definir operações API

Adicione as seguintes operações para corresponder à superfície da API do GeoCatalog. As operações wildcard (/*) encaminham todos os pedidos correspondentes para o backend. As operações explícitas de recolha permitem-lhe aplicar políticas específicas da coleção mais tarde para controlo de acesso.

Nome de exibição Método Modelo de URL
GET GET /*
Obtenha itens de coleção GET /stac/collections/{collection_id}/items
Obtenha a coleção de singles GET /stac/collections/{collection_id}
Obtenha subrecursos da coleção GET /stac/collections/{collection_id}/*
POST POST /*

Para adicionar cada operação:

  1. Selecione a API que criou.
  2. Selecione + Adicionar operação.
  3. Introduza o nome de visualização, o Método e o modelo de URL da tabela anterior.
  4. Selecione Guardar.

Configurar a política ao nível da API

A política ao nível da API trata da autenticação e da reescrita de URLs para toda a API. Esta política adquire um token da identidade gerida atribuída pelo utilizador e anexa-o a todos os pedidos encaminhados para o backend do GeoCatalog.

  1. Selecione a API que criou e depois selecione Todas as operações.
  2. Na seção Processamento de entrada, selecione o ícone </> (editor de código).
  3. Substitua o conteúdo da política pela seguinte política:
<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>

Substitua os seguintes espaços reservados:

Marcador de Posição Value
<managed-identity-client-id> O ID de cliente da identidade gerida atribuída pelo utilizador ao Gerenciamento de API (APIM)
<name>.<id>.<region> Componentes do endpoint do seu GeoCatalog
<apim-name> O nome da sua instância APIM

A tabela seguinte descreve cada elemento de política:

Elemento de política Purpose
authentication-managed-identity Adquire um token para o https://geocatalog.spatio.azure.com público usando a identidade gerida especificada e anexa-o ao pedido de saída.
set-header (apagar Accept-Encoding) O cabeçalho Accept-Encoding é removido dos pedidos recebidos. Veja Porquê Remover a Aceitação de Codificação.
find-and-replace Reescreve o URL do backend do GeoCatalog nos corpos de resposta para o URL do APIM gateway. Sem esta reescrita, as ligações STAC (self, root, parent, e assim sucessivamente) expõem o URL do backend aos chamadores.

Por que remover Accept-Encoding

Clientes como o Python requests e httpx enviam Accept-Encoding: gzip, deflate por defeito. Quando o backend recebe este cabeçalho, devolve uma resposta comprimida. As políticas de saída do APIM, como find-and-replace, operam no corpo cru da resposta e não conseguem descomprimi-lo, por isso não produzem efeito algum de maneira discreta. Remover o cabeçalho força o backend a devolver uma resposta não comprimida que as políticas de saída podem processar.

Note

curl e wget não enviam Accept-Encoding por padrão. Isto significa que as políticas de saída parecem funcionar corretamente quando se testa com essas ferramentas. A inconsistência só aparece com clientes que pedem compressão.

Impor controlo de acesso ao nível da coleção

Por defeito, um GeoCatálogo expõe todas as suas coleções a qualquer chamador autenticado. Para restringir quais as coleções visíveis através do APIM, aplique políticas ao nível operacional que bloqueiem a descoberta ampla do STAC e imponham uma lista de permissões.

Defina as coleções permitidas

Crie um valor nomeado no APIM para armazenar a lista de IDs de coleções permitidos:

  1. Na sua instância APIM, selecione Valores nomeados na barra lateral esquerda.
  2. Selecione + Adicionar.
  3. Defina o nome para allowed-collections.
  4. Defina o Valor para uma lista separada por vírgulas de IDs de coleção permitidos (por exemplo, sentinel-2-l2a,landsat-8-c2-l2).
  5. Selecione Guardar.

Bloqueie a página de destino e a lista de coleções

Bloqueia as rotas que revelam todas as coleções do catálogo. Adicione as seguintes operações e anexe uma política que retorne 404 imediatamente:

Nome de exibição Método Modelo de URL
Raiz de bloco GET /
Coleções em blocos GET /stac/collections

Aplicar a seguinte política ao nível operacional a ambas as operações:

<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>

O endpoint STAC /stac/search aceita um collections parâmetro - como uma cadeia de consulta no GET ou no corpo JSON no POST. Sem proteções, um interlocutor podia pesquisar em todas as coleções do catálogo. As seguintes políticas validam que apenas são solicitadas coleções do conjunto permitido.

Adicionar duas operações:

Nome de exibição Método Modelo de URL
GET pesquisa GET /stac/search
Pesquisa POST POST /stac/search

Política GET /stac/search

Esta política valida o collections parâmetro da consulta. Cada valor separado por vírgulas deve estar no conjunto permitido. Pedidos sem parâmetro collections são rejeitados com 403 Forbidden.

Aplique a seguinte política à operação de pesquisa 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

Expressões de políticas APIM executam-se num ambiente C# restrito. Use condition='@{...}' (atributo com aspas simples) para que as aspas duplas funcionem dentro da expressão. Evite parâmetros de tipo genéricos (por exemplo, GetValueOrDefault<string>) e lambdas LINQ – opte por fazer conversões explícitas e utilizar estruturas de repetição foreach em vez disso.

Política de POST /stac/search

Esta política analisa o corpo JSON e valida o collections array. Pedidos sem parâmetro collections são rejeitados com 403 Forbidden.

Aplique a seguinte política à operação de pesquisa 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>

Aplicar coleções permitidas nos pontos de extremidade de coleções

Sem operações explícitas, os pedidos como GET /stac/collections/sentinel-2-l2a ou GET /stac/collections/sentinel-2-l2a/items acabam no GET /* wildcard e chegam ao backend sem qualquer verificação ao nível da coleção. Aplique a política de parâmetros de caminho que valida collection_id em relação a {{allowed-collections}} às seguintes operações que criou em Definir operações de API:

Nome de exibição Método Modelo de URL
Obtenha a coleção de singles GET /stac/collections/{collection_id}
Obtenha subrecursos da coleção GET /stac/collections/{collection_id}/*
Obtenha itens de coleção GET /stac/collections/{collection_id}/items

Aplicar a seguinte política a todas as três operações:

<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>

Impor coleções permitidas nas rotas dos tokens SAS

A API SAS do GeoCatalog permite aos chamadores gerar tokens de armazenamento e assinar HREFs de ativos. Sem restrições, um chamador podia obter tokens para qualquer coleção. As seguintes políticas garantem que apenas as coleções permitidas possam ser acedidas.

Adicione as seguintes operações:

Nome de exibição Método Modelo de URL
Obter token SAS GET /sas/token/{collection_id}
Sinal do bloco SAS GET /sas/sign

Aplique a 404 política de bloqueio (igual ao bloco raiz e de coleções) à operação de sinal de bloqueio SAS. Os chamadores devem usar /sas/token/{collection_id} em vez disso para obter tokens SAS ao nível da coleção.

Aplique a seguinte política à operação do token 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>

Impor recolhas permitidas em rotas de dados

Os /data/mosaic/ endpoints fornecem renderização de blocos, cortes em caixas delimitadoras e registo de termos de pesquisa. São necessários dois grupos de políticas:

  1. Pesquisa de registos - valida o collections array no corpo JSON.
  2. Todas as outras rotas de recolha - validem o collectionId parâmetro de caminho.

Adicione as seguintes operações:

Nome de exibição Método Modelo de URL
Consulta de registos POST POST /data/mosaic/register
Recolha de dados GET GET /data/mosaic/collections/{collectionId}/*

Política POST /data/mosaic/registo

Esta política valida a matriz collections no corpo JSON em relação ao conjunto permitido. Pedidos sem parâmetro collections são rejeitados.

<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}/* política

Esta política valida o collectionId parâmetro de caminho em relação ao conjunto permitido. Aplique esta política a ambas as operações de recolha de dados do 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>

Perguntas frequentes

Como posso atualizar a lista de coleções permitidas?

Editar o valor nomeado allowed-collections na instância APIM. Não são necessárias alterações de política.

O que acontece se um chamador omitir o parâmetro de cobrança?

O pedido é rejeitado com 403 Forbidden. Os chamadores devem sempre especificar quais as coleções que querem pesquisar.