Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

NGINX | 17 de junho de 2024

Há muito tempo, o NGINX é reconhecido como uma das principais opções para operar servidores web de alto desempenho e balanceadores de carga. No entanto, com a ascensão das arquiteturas de microsserviços e a necessidade de um gerenciamento eficiente de APIs, o NGINX também se tornou uma escolha popular para a criação de gateways de API.

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

Forneceremos instruções passo a passo e informações sobre como aproveitar o NGINX Plus para otimizar seus processos de gerenciamento de APIs e garantir o melhor desempenho possível para suas aplicações.

Apresentando o API Gateway

Um gateway de API funciona como um hub central para gerenciar e proteger a comunicação entre clientes e serviços de backend. Ele atua como um proxy reverso entre o cliente e os servidores de backend, encaminhando as solicitações recebidas e distribuindo-as aos serviços apropriados. Isso permite uma comunicação mais eficiente entre o cliente e os serviços, além de possibilitar que o gateway de API execute tarefas como autenticação, autorização, limitação de taxa, e armazenamento em cache.

Além disso, um gateway de API pode atuar como uma camada de segurança, protegendo os serviços de backend contra possíveis ameaças e ataques. Ele pode aplicar medidas de segurança, como criptografia, autenticação baseada em tokens e controle de acesso, garantindo que apenas usuários autorizados possam acessar os serviços. Ao consolidar e gerenciar esses recursos de segurança em um único local, o gateway de API ajuda a simplificar a arquitetura geral de segurança do sistema e a reduzir a complexidade da implementação de medidas de segurança em vários serviços.

Projeto de API Declarativa do NGINX

O projeto NGINX Declarative API, mantido pela comunidade, fornece um conjunto de APIs REST declarativas para o NGINX Instance Manager.

Ele pode ser usado para gerenciar os ciclos de vida das configurações do NGINX Plus e criar configurações do NGINX Plus usando definições de serviço em JSON. A integração com GitOps é compatível quando usada com o NGINX Instance Manager: a fonte da verdade é verificada quanto a atualizações nos objetos referenciados, e as configurações do NGINX são mantidas automaticamente em sincronia.

Os esquemas OpenAPI podem ser usados para configurar automaticamente o NGINX como um gateway de API. A criação de portais para desenvolvedores é compatível por meio do Redocly.

Pré-requisitos

Para executar o conteúdo deste blog, você precisa de:

  • Uma instância em execução do NGINX Instance Manager
  • Uma assinatura (ou uma avaliação gratuita de 30 dias) do NGINX Plus e do NGINX App Protect WAF. O NGINX Agent deve estar instalado para se conectar ao NGINX Instance Manager e fazer parte do grupo de instâncias declarativeAPITest instance group
  • Um host Linux (bare metal ou VM) com docker e docker-compose para executar o projeto NGINX Declarative API
  • Postman para enviar as solicitações da API declarativa
  • Um host cliente para executar o Postman

Visão geral do laboratório

Após instalar e executar todos os pré-requisitos, o NGINX Instance Manager mostra a instância do NGINX Plus como online com o NGINX App Protect WAF.

Visão geral do NGINX Instance Manager

A instância do NGINX Plus faz parte do Grupo de Instâncias declarativeAPITest Instance Group

Grupos de instâncias do NGINX Instance Manager

Implantando a API declarativa

O projeto NGINX Declarative API utiliza a API REST fornecida pelo NGINX Instance Manager e oferece uma abstração declarativa baseada em JSON. Siga estas instruções para executar o projeto Declarative API:

1. Execute docker ps para verificar se o Docker está em execução:


f5@ubuntu:~$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

2. No host Linux, clone o repositório do GitHub:


f5@ubuntu:~$ git clone https://github.com/f5devcentral/NGINX-Declarative-API/
Cloning into 'NGINX-Declarative-API'...
remote: Enumerating objects: 4072, done.
remote: Counting objects: 100% (1982/1982), done.
remote: Compressing objects: 100% (1332/1332), done.
remote: Total 4072 (delta 668), reused 876 (delta 609), pack-reused 2090
Receiving objects: 100% (4072/4072), 19.05 MiB | 4.88 MiB/s, done.
Resolving deltas: 100% (1154/1154), done.
f5@ubuntu:~$

3. Acesse o diretório docker-compose:


f5@ubuntu:~$ cd NGINX-Declarative-API/contrib/docker-compose/

4. Use o script nginx-dapi.sh para iniciar todos os contêineres por meio do docker-compose. Durante a inicialização, todas as imagens do Docker são criadas automaticamente:


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ ./nginx-dapi.sh -c start
-> Updating docker images
[+] Pulling 11/11
[...]
-> Deploying NGINX Declarative API
[+] Running 4/4
 ✔ Network nginx-dapi_dapi-network  Created 0.1s 
 ✔ Container redis                  Started 1.5s 
 ✔ Container devportal              Started 1.5s 
 ✔ Container nginx-dapi             Started

5. Verifique os contêineres Docker em execução:


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ docker ps
CONTAINER ID   IMAGE                             COMMAND                  CREATED         STATUS         PORTS                                       NAMES
e29a2f783da2   nginx-declarative-api             "/deployment/env/bin…"   5 minutes ago   Up 5 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   nginx-dapi
97142840eaf7   redis                             "docker-entrypoint.s…"   5 minutes ago   Up 5 minutes   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   redis
6b50c0426643   nginx-declarative-api-devportal   "/deployment/src/sta…"   5 minutes ago   Up 5 minutes   0.0.0.0:5001->5000/tcp, :::5001->5000/tcp   devportal

6. No host cliente, execute o Postman e importe a coleção da API declarativa do NGINX disponível em https://raw.githubusercontent.com/f5devcentral/NGINX-Declarative-API/main/contrib/postman/NGINX%20Declarative%20API.postman_collection.json

7. Edite as variáveis da coleção do Postman para ajustá-las ao seu ambiente:

API declarativa do NGINX

8. Defina as seguintes variáveis:

  • ncg_host - nome do host ou endereço IP do host Linux em que o docker-compose da API Declarativa está em execução
  • ncg_port - porta TCP para a API Declarativa do NGINX: 5000 é a porta padrão
  • nim_host - URL base do NGINX Instance Manager (por exemplo, https://nms.k8s.ie.ff.lan)
  • nim_username - nome de usuário para autenticação no NGINX Instance Manager
  • nim_password - senha de autenticação do NGINX Instance Manager
Variáveis da API declarativa do NGINX

9. Salve todas as alterações no Postman

10. Na coleção do Postman, navegue até Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF e abra a solicitação

Coleções de APIs declarativas do NGINX

A declaração JSON é a seguinte:


{
    "output": {
        "type": "nms",
        "nms": {
            "url": "{{nim_host}}",
            "username": "{{nim_username}}",
            "password": "{{nim_password}}",
            "instancegroup": "{{nim_instancegroup}}",
            "synctime": 0,
            "modules": [
                "ngx_http_app_protect_module"
            ],
            "certificates": [
                {
                    "type": "certificate",
                    "name": "test_cert",
                    "contents": {
                        "content": "{{github_gitops_root}}/v4.2/testcert.crt"
                    }
                },
                {
                    "type": "key",
                    "name": "test_key",
                    "contents": {
                        "content": "{{github_gitops_root}}/v4.2/testcert.key"
                    }
                }
            ],
            "policies": [
                {
                    "type": "app_protect",
                    "name": "production-policy",
                    "active_tag": "xss-blocked",
                    "versions": [
                        {
                            "tag": "xss-blocked",
                            "displayName": "Production Policy - XSS blocked",
                            "description": "This is a production-ready policy - XSS blocked",
                            "contents": {
                                "content": "{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json"
                            }
                        },
                        {
                            "tag": "xss-allowed",
                            "displayName": "Production Policy - XSS allowed",
                            "description": "This is a production-ready policy - XSS allowed",
                            "contents": {
                                "content": "{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json"
                            }
                        }
                    ]
                }
            ]
        }
    },
    "declaration": {
        "http": {
            "servers": [
                {
                    "name": "Petstore API",
                    "names": [
                        "apigw.nginx.lab"
                    ],
                    "resolver": "8.8.8.8",
                    "listen": {
                        "address": "0.0.0.0:443",
                        "http2": true,
                        "tls": {
                            "certificate": "test_cert",
                            "key": "test_key",
                            "ciphers": "DEFAULT",
                            "protocols": [
                                "TLSv1.2",
                                "TLSv1.3"
                            ]
                        }
                    },
                    "log": {
                        "access": "/var/log/nginx/apigw.nginx.lab-access_log",
                        "error": "/var/log/nginx/apigw.nginx.lab-error_log"
                    },
                    "locations": [
                        {
                            "uri": "/petstore",
                            "urimatch": "prefix",
                            "apigateway": {
                                "openapi_schema": {
                                    "content": "http://petstore.swagger.io/v2/swagger.json"
                                },
                                "api_gateway": {
                                    "enabled": true,
                                    "strip_uri": true,
                                    "server_url": "https://petstore.swagger.io/v2"
                                },
                                "developer_portal": {
                                    "enabled": true,
                                    "uri": "/petstore-devportal.html"
                                },
                                "authentication": {
                                    "client": [
                                        {
                                            "profile": "Petstore JWT Authentication"
                                        }
                                    ],
                                    "enforceOnPaths": true,
                                    "paths": [
                                        "/user/login",
                                        "/user/logout"
                                    ]
                                },
                                "authorization": [
                                    {
                                        "profile": "JWT role based authorization",
                                        "enforceOnPaths": true,
                                        "paths": [
                                            "/user/login",
                                            "/user/logout"
                                        ]
                                    }
                                ],
                                "rate_limit": [
                                    {
                                        "profile": "petstore_ratelimit",
                                        "httpcode": 429,
                                        "burst": 0,
                                        "delay": 0,
                                        "enforceOnPaths": true,
                                        "paths": [
                                            "/user/login",
                                            "/user/logout"
                                        ]
                                    }
                                ]
                            },
                            "log": {
                                "access": "/var/log/nginx/petstore-access_log",
                                "error": "/var/log/nginx/petstore-error_log"
                            },
                            "app_protect": {
                                "enabled": true,
                                "policy": "production-policy",
                                "log": {
                                    "profile_name": "secops_dashboard",
                                    "enabled": true,
                                    "destination": "127.0.0.1:514"
                                }
                            }
                        }
                    ]
                }
            ],
            "rate_limit": [
                {
                    "name": "petstore_ratelimit",
                    "key": "$binary_remote_addr",
                    "size": "10m",
                    "rate": "2r/s"
                }
            ],
            "authentication": {
                "client": [
                    {
                        "name": "Petstore JWT Authentication",
                        "type": "jwt",
                        "jwt": {
                            "realm": "Petstore Authentication",
                            "key": "{\"keys\": [{\"k\":\"ZmFudGFzdGljand0\",\"kty\":\"oct\",\"kid\":\"0001\"}]}",
                            "cachetime": 5
                        }
                    }
                ]
            },
            "authorization": [
                {
                    "name": "JWT role based authorization",
                    "type": "jwt",
                    "jwt": {
                        "claims": [
                            {
                                "name": "roles",
                                "value": [
                                    "~(devops)"
                                ],
                                "errorcode": 403
                            }
                        ]
                    }
                }
            ]
        }
    }
}

A seção output define:

  • O servidor NGINX Instance Manager no qual a API Declarativa publicará a configuração do NGINX
  • Certificados e chaves TLS — eles podem ser referenciados pela URL da fonte oficial em que estão armazenados
  • Políticas de segurança do WAF do NGINX App Protect — elas podem ser referenciadas pela URL da fonte oficial em que estão armazenadas

A seção declaration descreve:

  • O servidor NGINX a ser criado
  • Se o descarregamento de TLS é realizado ou não
  • Onde registrar as entradas de acesso e de erro
  • O URI base /petstore em que a configuração do API Gateway será implantada e disponibilizada aos clientes
  • A configuração do API Gateway e o portal do desenvolvedor a serem publicados
  • Como o NGINX App Protect WAF é habilitado, qual política de segurança deve ser usada e onde registrar as violações de segurança
  • Como autenticar e autorizar solicitações de clientes

A seção de declaração do API Gateway descreve como a API declarativa entregará seu resultado.

O esquema OpenAPI é referenciado por meio de sua URL completa:


"apigateway": {
    "openapi_schema": {
        "content": "http://petstore.swagger.io/v2/swagger.json"
    },

A criação da configuração do NGINX API Gateway é solicitada, e o servidor upstream é definido. Quando o NGINX encaminha as solicitações ao upstream por meio de proxy reverso, o URI base /petstore é removido:


"api_gateway": {
        "enabled": true,
        "strip_uri": true,
        "server_url": "https://petstore.swagger.io/v2"
    },

É necessário criar e implantar o portal do desenvolvedor em um URI específico:


"developer_portal": {
        "enabled": true,
        "uri": "/petstore-devportal.html"
    },

A autenticação de clientes com base no perfil de autenticação de cliente especificado é obrigatória para /user/login e /user/logout:


"authentication": {
        "client": [
            {
                "profile": "Petstore JWT Authentication"
            }
        ],
        "enforceOnPaths": true,
        "paths": [
            "/user/login",
            "/user/logout"
        ]
    },

A autorização de clientes com base no perfil de autorização de cliente especificado é aplicada a /user/login e /user/logout:


"authorization": [
        {
            "profile": "JWT role based authorization",
            "enforceOnPaths": true,
            "paths": [
                "/user/login",
                "/user/logout"
            ]
        }
    ],

A limitação de taxa é aplicada a /user/login e /user/logout com base no perfil especificado:


"rate_limit": [
        {
            "profile": "petstore_ratelimit",
            "httpcode": 429,
            "burst": 0,
            "delay": 0,
            "enforceOnPaths": true,
            "paths": [
                "/user/login",
                "/user/logout"
            ]
        }
    ]
},

12. Use o botão Enviar do Postman para publicar a solicitação na API declarativa. A resposta será semelhante a:


{
    "code": 200,
    "content": {
        "createTime": "2024-04-26T17:09:10.419574328Z",
        "details": {
            "failure": [],
            "pending": [],
            "success": [
                {
                    "name": "vm-test"
                }
            ]
        },
        "id": "1060ec49-120e-45ca-820b-5203c8b3538d",
        "message": "Instance Group config successfully published to declarativeAPITest",
        "status": "successful",
        "updateTime": "2024-04-26T17:09:10.881509913Z"
    },
    "configUid": "eecf1da6-9d8f-4e44-89cc-a470af79379d"
}

13. Nesta etapa, a instância do NGINX está configurada como um gateway de API, a segurança do WAF está habilitada e o portal do desenvolvedor está publicado.

Testando o API Gateway

Observação: pressupõe-se que o FQDN apigw.nginx.lab seja resolvido para o endereço IP da máquina virtual em que a instância do NGINX está em execução

1. Acesse o diretório jwt:


f5@ubuntu:~$ cd ~/NGINX-Declarative-API/contrib/gitops-examples/jwt

2. Acesse um endpoint de API REST não autenticado:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/store/inventory
HTTP/2 200 
date: Fri, 26 Apr 2024 17:13:54 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization

{"totvs":5,"aut":1,"FORsold":1,[...]

3. Limitação de taxa:


    $ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login;curl -w '\n' -ki 
    https://apigw.nginx.lab/petstore/user/login
    HTTP/2 401 
    date: Fri, 26 Apr 2024 17:14:51 GMT
    content-type: text/html
    content-length: 179
    www-authenticate: Bearer realm="Petstore Authentication"
    <html>
    <head><title>401 Authorization Required</title></head>
    <body>
    <center><h1>401 Authorization Required</h1></center>
    <hr><center>nginx/1.25.3</center>
    </body>
    </html>
    HTTP/2 429 
    date: Fri, 26 Apr 2024 17:14:51 GMT
    content-type: text/html
    content-length: 169
    <html>
    <head><title>429 Too Many Requests</title></head>
    <body>
    <center><h1>429 Too Many Requests</h1></center>
    <hr><center>nginx/1.25.3</center>
    </body>
    </html>
    

4. Autenticação e autorização válida:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.devops`"
HTTP/2 200 
date: Fri, 26 Apr 2024 17:15:41 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
x-expires-after: Fri Apr 26 18:15:41 UTC 2024
x-rate-limit: 5000

{"code":200,"type":"unknown","message":"logged in user session:1714151741883"}

5. Autenticação e autorização inválida:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.guest`"
HTTP/2 403 
date: Fri, 26 Apr 2024 17:16:07 GMT
content-type: text/html
content-length: 153
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>

6. NGINX App Protect WAF e violação de segurança de Cross-Site Scripting:


$ curl -w '\n' -ki "https://apigw.nginx.lab/petstore/store/inventory?
"
HTTP/2 200 
content-type: text/html; charset=utf-8
cache-control: no-cache
pragma: no-cache
content-length: 246

7. O portal do desenvolvedor pode ser acessado navegando até


https://apigw.nginx.lab/petstore/petstore-devportal.html

Comece agora

Para experimentar as soluções NGINX abordadas nesta publicação, inicie hoje mesmo uma avaliação gratuita de 30 dias ou entre em contato conosco para conversar sobre seus casos de uso:

Baixe o NGINX Agent – é gratuito e de código aberto.

Compartilhar
Tags: F5 NGINX, 2024

Sobre o autor

Fabrizio Fiorucci
Fabrizio FiorucciArquiteto de Soluções NGINX | F5 (EMEA)

Mais publicações de Fabrizio Fiorucci

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

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
NGINX | 17/06/2024

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
NGINX | 14/07/2021

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
NGINX | 02/04/2019

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
NGINX | 24/05/2018

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
NGINX | 19/04/2016

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.