BLOG | NGINX

Do OpenAPI ao NGINX como um API Gateway usando uma API declarativa

Fabrizio Fiorucci Miniatura
Fabrizio Fiorucci
Publicado em 17 de junho de 2024

O NGINX é reconhecido há muito tempo como uma das principais opções para alimentar servidores web e balanceadores de carga de alto desempenho. No entanto, com o surgimento de arquiteturas de microsserviços e a necessidade de gerenciamento eficiente de API, o NGINX também se tornou uma escolha popular para criar gateways de API.

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

Forneceremos instruções passo a passo e insights sobre como aproveitar o NGINX Plus para otimizar seus processos de gerenciamento de API e garantir o desempenho ideal para seus aplicativos.

Apresentando o API Gateway

Um gateway de API serve como um hub central para gerenciar e proteger a comunicação entre clientes e serviços de backend. Ele atua como um proxy reverso que fica entre o cliente e os servidores de backend, roteando solicitações recebidas e distribuindo-as para os serviços apropriados. Isso permite uma comunicação mais eficiente entre o cliente e os serviços, além de permitir que o gateway de API lide com 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 potenciais ameaças e ataques. Ele pode aplicar medidas de segurança como criptografia, autenticação baseada em tokens e controle de acesso, garantindo que somente usuários autorizados possam acessar os serviços. Ao consolidar e gerenciar esses recursos de segurança em um único lugar, 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 NGINX

O projeto NGINX Declarative API com suporte da comunidade fornece um conjunto de API REST declarativa para o NGINX Instance Manager .

Ele pode ser usado para gerenciar ciclos de vida de configuração do NGINX Plus e para criar configurações do NGINX Plus usando definições de serviço JSON. A integração do GitOps é suportada quando usada com o NGINX Instance Manager: a fonte da verdade é verificada para atualizações de objetos referenciados e as configurações do NGINX são mantidas automaticamente sincronizadas.

Os esquemas OpenAPI podem ser usados para configurar automaticamente o NGINX como um API Gateway. A criação do portal do desenvolvedor é suportada pelo Redocly .

Pré-requisitos

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

  • Uma instância em execução do NGINX Instance Manager
  • Uma assinatura (ou teste gratuito de 30 dias ) para NGINX Plus e NGINX App Protect WAF . O agente NGINX deve ser instalado para se conectar ao NGINX Instance Manager e fazer parte do grupo de instâncias declarativeAPITest
  • Um host Linux (bare metal ou VM) com docker e docker-compose para executar o projeto NGINX Declarative API
  • O carteiro deve enviar as solicitações declarativas da API
  • 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.

A instância NGINX Plus faz parte do grupo de instâncias declarativeAPITest

Implementando a API Declarativa

O projeto da API declarativa do NGINX depende da API REST fornecida pelo NGINX Instance Manager e fornece a abstração declarativa baseada em JSON. Siga estas instruções para executar o Projeto de API Declarativa:

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 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. Mude para 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 do cliente, execute o Postman e importe a coleção da API declarativa do NGINX 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 Postman para ajustá-las ao seu ambiente:

8. Defina as seguintes variáveis:

  • ncg_host - nome do host ou endereço IP do host Linux onde a API declarativa docker-compose está sendo executada
  • ncg_port - Porta TCP para a API declarativa NGINX: 5000 é o padrão
  • nim_host - URL base do gerenciador de instâncias do NGINX (por exemplo, https://nms.k8s.ie.ff.lan)
  • nim_username - nome de usuário de autenticação do NGINX Instance Manager
  • nim_password - senha de autenticação do NGINX Instance Manager

9. Salvar todas as alterações no Postman

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

A declaração JSON tem a seguinte aparência:


{
    "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 de saída define:

  • O servidor NGINX Instance Manager da API Declarativa publicará a configuração do NGINX para
  • Certificados e chaves TLS - eles podem ser referenciados pela URL da fonte da verdade onde estão armazenados
  • O aplicativo NGINX protege as políticas de segurança do WAF - elas podem ser referenciadas pela URL da fonte da verdade onde estão armazenadas

A seção de declaração descreve:

  • O servidor NGINX a ser criado
  • Se o descarregamento TLS é executado ou não
  • Onde registrar entradas de acesso e erro
  • O URI base /petstore onde a configuração do API Gateway será implantada e disponibilizada aos clientes
  • A configuração do API Gateway e o portal do desenvolvedor serão publicados
  • Como o NGINX App Protect WAF é habilitado, qual política de segurança usar e onde registrar 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 faz solicitações de proxy reverso para o upstream, o URI base /petstore é removido:


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




A criação e implantação do portal do desenvolvedor sob um URI específico é solicitada:


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


A autenticação de clientes com base no perfil de autenticação de cliente especificado é aplicada 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 para /user/login e /user/logout :


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




A limitação de taxa é aplicada para /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 Postman Send para publicar a solicitação na API declarativa. A resposta é 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 fase, a instância do NGINX é configurada como um API Gateway, a segurança do WAF é aplicada e o portal do desenvolvedor é publicado.

Testando o API Gateway

Observação: presume-se que o FQDN apigw.nginx.lab seja resolvido para o endereço IP da máquina virtual onde a instância NGINX está sendo executada

1. Mude para o diretório jwt :


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


2. Acesse um ponto de extremidade da 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. Violação de segurança do NGINX App Protect WAF e 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
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is: 7283327928460093545<br><br><a href='javascript:history.back();'>[Go Back]</a></body></html>

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


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


Iniciar

Para experimentar as soluções NGINX discutidas nesta postagem, inicie um teste gratuito de 30 dias hoje mesmo ou entre em contato conosco para discutir seus casos de uso:

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


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."