Este é o primeiro post da nossa série sobre a implantação do NGINX Open Source e do NGINX Plus como gateway de API:
- Este post fornece instruções detalhadas de configuração para vários casos de uso. Publicado originalmente em 2018, ele foi atualizado para refletir as práticas recomendadas atuais de configuração de APIs, usando blocos aninhados de
locationpara encaminhar solicitações, em vez de regras de reescrita. - Parte 2 amplia esses casos de uso e analisa diversas medidas de proteção que podem ser aplicadas para proteger e tornar seguros os serviços de API de backend em produção.
- Parte 3 explica como implantar o NGINX Open Source e o NGINX Plus como um gateway de API para serviços gRPC.
Observação: Salvo indicação em contrário, todas as informações desta publicação se aplicam tanto ao NGINX Open Source quanto ao NGINX Plus. Para facilitar a leitura, no restante do blog, ambos serão chamados simplesmente de “NGINX”.
No centro das arquiteturas de aplicações modernas está a API HTTP. O HTTP permite que as aplicações sejam desenvolvidas rapidamente e mantidas com facilidade. A API HTTP oferece uma interface comum, independentemente da escala da aplicação, desde um microsserviço de finalidade única até um monólito abrangente. Com o uso do HTTP, os avanços na entrega de aplicações web que dão suporte a propriedades da Internet em hiperescala também podem ser usados para proporcionar uma entrega de APIs confiável e de alto desempenho.
Para uma excelente introdução à importância dos gateways de API para aplicações de microsserviços, consulte Criando microsserviços: como usar um gateway de API em nosso blog.
Como líder entre os proxies reversos e balanceadores de carga leves e de alto desempenho, o NGINX conta com os recursos avançados de processamento HTTP necessários para lidar com o tráfego de APIs. Isso faz do NGINX a plataforma ideal para criar um gateway de API. Nesta publicação do blog, descrevemos vários casos de uso comuns de gateways de API e mostramos como configurar o NGINX para lidar com eles de maneira eficiente, escalável e fácil de manter. Descrevemos uma configuração completa que pode servir de base para uma implantação em produção.
Apresentando a API de armazém
A principal função do gateway de API é fornecer um ponto de entrada único e consistente para várias APIs, independentemente de como elas são implementadas ou implantadas no backend. Nem todas as APIs são aplicações de microsserviços. Nosso gateway de API precisa gerenciar APIs existentes, monólitos e aplicações que estejam passando por uma transição parcial para microsserviços.
Nesta publicação do blog, fazemos referência a uma API hipotética para gerenciamento de estoque, a “API de Armazém”. Usamos exemplos de código de configuração para ilustrar diferentes casos de uso. A API de Armazém é uma API RESTful que recebe solicitações JSON e produz respostas JSON. No entanto, o uso de JSON não é uma limitação nem um requisito do NGINX quando implantado como um gateway de API; o NGINX é independente do estilo arquitetural e dos formatos de dados usados pelas próprias APIs.
A API Warehouse é implementada como um conjunto de microsserviços independentes e publicada como uma única API. Os recursos de estoque e preços são implementados como serviços separados e implantados em back-ends distintos. Portanto, a estrutura de caminhos da API é:
api
└── warehouse
├── inventory
└── pricingPor exemplo, para consultar o estoque atual do armazém, um aplicativo cliente faz uma solicitação HTTP GET para /api/warehouse/inventory.

Organizando a configuração do NGINX
Uma das vantagens de usar o NGINX como gateway de API é que ele pode desempenhar essa função e, ao mesmo tempo, atuar como proxy reverso, balanceador de carga e servidor web para o tráfego HTTP existente. Se o NGINX já fizer parte da sua infraestrutura de entrega de aplicações, geralmente não será necessário implantar um gateway de API separado. No entanto, alguns dos comportamentos padrão esperados de um gateway de API diferem daqueles esperados para o tráfego baseado em navegador. Por esse motivo, separamos a configuração do gateway de API de qualquer configuração existente (ou futura) para o tráfego baseado em navegador.
Para obter essa separação, criamos um layout de configuração compatível com uma instância multiuso do NGINX e que oferece uma estrutura conveniente para automatizar a implantação de configurações por meio de pipelines de CI/CD. A estrutura de diretórios resultante em /etc/nginx é semelhante a esta.
etc/
└── nginx/
├── api_conf.d/ ………………………………… Subdirectory for per-API configuration
│ └── warehouse_api.conf …… Definition and policy of the Warehouse API
├── api_backends.conf ………………… The backend services (upstreams)
├── api_gateway.conf …………………… Top-level configuration for the API gateway server
├── api_json_errors.conf ………… HTTP error responses in JSON format
├── conf.d/
│ ├── ...
│ └── existing_apps.conf
└── nginx.confOs diretórios e nomes de arquivo de toda a configuração do gateway de API têm o prefixo api_. Cada um desses arquivos e diretórios habilita um recurso ou uma funcionalidade diferente do gateway de API, conforme explicado em detalhes abaixo. O arquivo warehouse_api.conf é um substituto genérico para os arquivos de configuração abordados abaixo, que definem a API Warehouse de diferentes maneiras.
Definindo o gateway de API de nível superior
Toda configuração do NGINX começa com o arquivo de configuração principal, nginx.conf. Para carregar a configuração do gateway de API, adicionamos uma diretiva include no bloco http do arquivo nginx.conf, que faz referência ao arquivo que contém a configuração do gateway, api_gateway.conf (linha 28, logo abaixo). Observe que o arquivo nginx.conf padrão usa uma diretiva include para carregar a configuração HTTP baseada em navegador do subdiretório conf.d (linha 29). Esta publicação do blog faz amplo uso da diretiva include para facilitar a leitura e permitir a automação de algumas partes da configuração.
Carregando exemplo de código…
O arquivo api_gateway.conf define o servidor virtual que disponibiliza o NGINX aos clientes como um gateway de API. Essa configuração disponibiliza todas as APIs publicadas pelo gateway de API em um único ponto de entrada, https://api.example.com/ (linha 9), protegido por TLS conforme configurado nas linhas 12 a 17. Observe que essa configuração usa exclusivamente HTTPS – não há nenhum listener HTTP sem criptografia. Esperamos que os clientes de API conheçam o ponto de entrada correto e estabeleçam conexões HTTPS por padrão.
Essa configuração foi projetada para ser estática – os detalhes de cada API e de seus serviços de backend são especificados nos arquivos referenciados pela diretiva include na linha 20. As linhas 23 a 26 tratam do gerenciamento de erros e são abordadas na seção Resposta a erros abaixo.
Carregando exemplo de código…
Backends de API de serviço único vs. microsserviços
Algumas APIs podem ser implementadas em um único backend, embora normalmente esperemos que haja mais de um por motivos de resiliência ou balanceamento de carga. Em APIs de microsserviços, definimos backends individuais para cada serviço; juntos, eles funcionam como a API completa. Aqui, nossa API de Armazém é implantada como dois serviços separados, cada um com vários backends.
Carregando exemplo de código…
Todos os serviços de API de backend, para todas as APIs publicadas pelo gateway de API, são definidos em api_backends.conf. Aqui, usamos vários pares de endereço IP e porta em cada bloco upstream para indicar onde o código da API está implantado, mas nomes de host também podem ser usados. Os assinantes do NGINX Plus também podem aproveitar o balanceamento de carga de DNS dinâmico para adicionar automaticamente novos backends à configuração em tempo de execução.
Definindo a API do armazém
A API Warehouse é definida por vários blocos location em uma configuração aninhada, conforme ilustrado no exemplo a seguir. O bloco location externo (/api/warehouse) identifica o caminho base, sob o qual os locais aninhados especificam os URIs válidos que são encaminhados aos serviços de API de back-end. O uso de um bloco externo permite definir políticas comuns que se aplicam a toda a API (neste exemplo, a configuração de registro em log na linha 6).
Carregando exemplo de código…
O NGINX tem um sistema altamente eficiente e flexível para associar o URI da solicitação a uma seção da configuração. A ordem das diretivas location não é importante – a correspondência mais específica é escolhida. Aqui, os locais aninhados nas linhas 10 e 14 definem dois URIs mais específicos do que o bloco location externo; a diretiva proxy_pass em cada bloco aninhado encaminha as solicitações para o grupo de upstream apropriado. A configuração da política é herdada do local externo, a menos que seja necessário fornecer uma política mais específica para determinados URIs.
Todos os URIs que não correspondem a um dos locais aninhados são processados pelo local externo, que inclui uma diretiva abrangente (linha 18) que retorna a resposta 404 (Não Encontrado) para todos os URIs inválidos.
Escolhendo entre uma definição ampla e uma definição precisa para APIs
Há duas abordagens para a definição de APIs: ampla e precisa. A abordagem mais adequada para cada API depende dos requisitos de segurança da API e de ser ou não desejável que os serviços de backend processem URIs inválidos.
No arquivo warehouse_api_simple.conf acima, usamos a abordagem ampla para a API Warehouse, definindo prefixos de URI nas linhas 10 e 14, de modo que uma URI que comece com um desses prefixos seja encaminhada por proxy ao serviço de backend apropriado. Com essa correspondência ampla de locais baseada em prefixos, as solicitações de API para as seguintes URIs são todas válidas:
/api/warehouse/inventory
/api/warehouse/inventory/
/api/warehouse/inventory/foo
/api/warehouse/inventoryfoo
/api/warehouse/inventoryfoo/bar/
Se a única consideração for encaminhar cada solicitação ao serviço de backend correto, a abordagem ampla proporcionará o processamento mais rápido e a configuração mais compacta. Por outro lado, uma abordagem mais precisa permite que o gateway de API compreenda todo o espaço de URIs da API, definindo explicitamente o caminho do URI de cada recurso de API disponível. Adotando a abordagem precisa, a configuração a seguir para o roteamento de URIs na API Warehouse usa uma combinação de correspondência exata (=) e expressões regulares (~) para definir todos os URIs válidos.
Carregando exemplo de código…
Essa configuração é mais detalhada, mas descreve com maior precisão os recursos implementados pelos serviços de backend. Isso oferece a vantagem de proteger os serviços de backend contra solicitações malformadas de clientes, ao custo de uma pequena sobrecarga adicional para a correspondência de expressões regulares. Com essa configuração, o NGINX aceita alguns URIs e rejeita outros como inválidos:
URIs válidos URIs inválidos/api/warehouse/inventory /api/warehouse/inventory//api/warehouse/inventory/shelf/foo /api/warehouse/inventoryfoo/api/warehouse/inventory/shelf/foo/box/bar /api/warehouse/inventory/shelf/api/warehouse/inventory/shelf/-/box/- /api/warehouse/inventory/shelf/foo/bar/api/warehouse/pricing/baz /api/warehouse/pricing /api/warehouse/pricing/baz/pub
O uso de uma definição precisa de API permite que os formatos existentes de documentação de API orientem a configuração do gateway de API. É possível automatizar as definições de API do NGINX com base na Especificação OpenAPI (anteriormente chamada de Swagger). Um script de exemplo para essa finalidade está disponível entre os Gists desta publicação do blog.
Reescrevendo solicitações de clientes para lidar com alterações incompatíveis
À medida que as APIs evoluem, às vezes é necessário fazer alterações que rompem a compatibilidade retroativa estrita e exigem a atualização dos clientes. Um exemplo disso ocorre quando um recurso de API é renomeado ou movido. Ao contrário de um navegador da Web, um gateway de API não pode enviar aos clientes um redirecionamento (código 301 (Movido permanentemente)) indicando o novo local. Felizmente, quando não é viável modificar os clientes da API, podemos reescrever as solicitações dos clientes em tempo real.
No exemplo a seguir, usamos a mesma abordagem geral adotada em warehouse_api_simple.conf acima, mas, neste caso, a configuração substitui uma versão anterior da API de Armazém, na qual o serviço de preços era implementado como parte do serviço de inventário. A diretiva rewrite na linha 3 converte as solicitações ao recurso de preços antigo em solicitações ao novo serviço de preços.
Carregando exemplo de código…
Respondendo a erros
Uma das principais diferenças entre APIs HTTP e o tráfego baseado em navegador é a forma como os erros são comunicados ao cliente. Quando o NGINX é implantado como um gateway de API, nós o configuramos para retornar os erros da maneira mais adequada aos clientes da API.
A configuração de nível superior do gateway de API inclui uma seção que define como tratar as respostas de erro.
Carregando exemplo de código…
A diretiva error_page na linha 23 especifica que, quando uma solicitação não corresponde a nenhuma das definições de API, o NGINX retorna o erro 400 (Solicitação inválida) em vez do erro padrão 404 (Não encontrado). Esse comportamento (opcional) exige que os clientes da API façam solicitações somente aos URIs válidos incluídos na documentação da API e impede que clientes não autorizados descubram a estrutura de URIs das APIs publicadas por meio do gateway de API.
A linha 24 refere-se a erros gerados pelos próprios serviços de backend. Exceções não tratadas podem conter rastreamentos de pilha ou outros dados confidenciais que não queremos enviar ao cliente. Essa configuração adiciona mais uma camada de proteção ao enviar uma resposta de erro padronizada ao cliente.
A lista completa de respostas de erro padronizadas é definida em um arquivo de configuração separado, referenciado pela diretiva include na linha 25, cujas primeiras linhas são mostradas abaixo. Esse arquivo pode ser modificado caso seja preferível usar um formato de erro diferente de JSON, alterando o valor de default_type na linha 26 de api_gateway.conf para que corresponda ao formato escolhido. Também é possível ter uma diretiva include separada na seção de políticas de cada API para referenciar outro arquivo de respostas de erro, que substituirão as respostas globais.
Carregando exemplo de código…
Com essa configuração implementada, uma solicitação de cliente para um URI inválido recebe a seguinte resposta. [terminal]$ curl -i https://api.example.com/foo HTTP/1.1 400 Bad Request Server: nginx/1.19.5 Content-Type: application/json Content-Length: 39 Connection: keep-alive {"status":400,"message":"Bad request"}[/terminal]Implementando a autenticação
É incomum publicar APIs sem algum tipo de autenticação para protegê-las. O NGINX oferece várias abordagens para proteger APIs e autenticar clientes de API. Para obter informações sobre abordagens que também se aplicam a solicitações HTTP comuns, consulte a documentação sobre listas de controle de acesso baseadas em endereço IP (ACLs), autenticação por certificado digital e autenticação HTTP Basic. Aqui, nosso foco são os métodos de autenticação específicos para APIs.
Autenticação por chave de API
As chaves de API são um segredo compartilhado conhecido pelo cliente e pelo gateway de API. Uma chave de API é, essencialmente, uma senha longa e complexa fornecida ao cliente da API como uma credencial de longo prazo. Criar chaves de API é simples – basta codificar um número aleatório, como neste exemplo.
$ openssl rand -base64 187B5zIqmRGXmrJTFmKa99vcitNa linha 2 do arquivo de configuração de nível superior do gateway de API, api_gateway.conf, incluímos um arquivo chamado api_keys.conf, que contém uma chave de API para cada cliente da API, identificado pelo nome do cliente ou por outra descrição. Veja o conteúdo desse arquivo:
Carregando exemplo de código…
As chaves de API são definidas em um bloco map. A diretiva map recebe dois parâmetros. O primeiro define onde encontrar a chave de API, neste caso, no cabeçalho HTTP apikey da solicitação do cliente, conforme capturado na variável $http_apikey. O segundo parâmetro cria uma nova variável ($api_client_name) e atribui a ela o valor do segundo parâmetro na linha em que o primeiro parâmetro corresponde à chave.
Por exemplo, quando um cliente apresenta a chave de API 7B5zIqmRGXmrJTFmKa99vcit, a variável $api_client_name é definida como client_one. Essa variável pode ser usada para verificar clientes autenticados e incluída em entradas de log para permitir auditorias mais detalhadas. O formato do bloco map é simples e fácil de integrar a fluxos de trabalho de automação que geram o arquivo api_keys.conf a partir de um repositório de credenciais existente.
Aqui, habilitamos a autenticação por chave de API alterando a configuração “ampla” (warehouse_api_simple.conf) para incluir uma diretiva auth_request na seção de políticas, que delega a decisão de autenticação a um local especificado.
Carregando exemplo de código…
Com a diretiva auth_request (linha 7), podemos, por exemplo, fazer com que a autenticação seja processada por um servidor de autenticação externo, como a introspecção de token OAuth 2.0. Neste exemplo, em vez disso, adicionamos a lógica de validação de chaves de API ao arquivo de configuração de nível superior do gateway de API, na forma do seguinte bloco location, chamado /_validate_apikey.
Carregando exemplo de código…
A diretiva internal na linha 30 significa que esse local não pode ser acessado diretamente por clientes externos (somente por auth_request). Os clientes devem apresentar sua chave de API no cabeçalho HTTP apikey. Se esse cabeçalho estiver ausente ou vazio (linha 32), enviaremos uma resposta 401 (Não autorizado) para informar ao cliente que a autenticação é necessária. A linha 35 trata do caso em que a chave de API não corresponde a nenhuma das chaves no bloco map – nesse caso, o parâmetro default na linha 2 de api_keys.conf define $api_client_name como uma string vazia – e enviaremos uma resposta 403 (Proibido) para informar ao cliente que a autenticação falhou. Se nenhuma dessas condições for atendida, a chave de API será válida e o local retornará uma resposta 204 (Sem conteúdo).
Com essa configuração, a API Warehouse agora implementa a autenticação por chave de API.
$ curl https://api.example.com/api/warehouse/pricing/item001{"status":401,"message":"Unauthorized"}
$ curl -H "apikey: thisIsInvalid" https://api.example.com/api/warehouse/pricing/item001
{"status":403,"message":"Forbidden"}
$ curl -H "apikey: 7B5zIqmRGXmrJTFmKa99vcit" https://api.example.com/api/warehouse/pricing/item001
{"sku":"item001","price":179.99}Autenticação JWT
Os JSON Web Tokens (JWTs) são cada vez mais usados para autenticação de APIs. O suporte nativo a JWT é exclusivo do NGINX Plus e permite validar JWTs conforme descrito em Autenticação de clientes de API com JWT e NGINX Plus em nosso blog. Para ver um exemplo de implementação, consulte Controle de acesso a métodos específicos na Parte 2.
Resumo
Este primeiro artigo de uma série apresenta em detalhes uma solução completa para implantar o NGINX Open Source e o NGINX Plus como gateway de API. O conjunto completo de arquivos abordados neste artigo pode ser consultado e baixado em nosso repositório do GitHub Gist.
Confira os outros posts desta série:
- Parte 2 explora casos de uso mais avançados para proteger serviços de back-end contra clientes mal-intencionados ou com comportamento inadequado.
- Parte 3 explica como implantar o NGINX como um gateway de API para serviços gRPC.
Para experimentar o NGINX Plus, inicie sua avaliação gratuita de 30 dias hoje mesmo ou entre em contato conosco para conversar sobre seus casos de uso.
Sobre o autor

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.
Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.
Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.