Tutorial do NGINX: proteja aplicações Kubernetes contra injeção de SQL

NGINX | 22 de março de 2022

Este tutorial é um dos quatro que colocam em prática os conceitos de Microservices March 2022: Redes no Kubernetes:

Quer orientações detalhadas sobre como usar o NGINX em ainda mais casos de uso de redes no Kubernetes? Baixe gratuitamente nosso eBook, Gerenciamento de tráfego do Kubernetes com o NGINX: um guia prático.

Você trabalha na área de TI de uma loja local popular que vende diversos produtos, de travesseiros a bicicletas. A loja está prestes a lançar sua primeira loja virtual, mas pediu a um especialista em segurança que realizasse um teste de invasão no site antes de disponibilizá-lo ao público. Infelizmente, o especialista em segurança encontrou um problema! A loja virtual está vulnerável a injeção de SQL. O especialista em segurança conseguiu explorar a vulnerabilidade do site para obter informações confidenciais do banco de dados, incluindo nomes de usuário e senhas.

Sua equipe recorreu a você — o engenheiro de Kubernetes — para salvar o dia. Felizmente, você sabe que a injeção de SQL (assim como outras vulnerabilidades) pode ser mitigada usando ferramentas de gerenciamento de tráfego do Kubernetes. Você já implantou um controlador Ingress para expor o aplicativo e, com uma única configuração, consegue garantir que a vulnerabilidade não possa ser explorada. Agora, a loja virtual pode ser lançada dentro do prazo. Muito bem!

Visão geral do laboratório e do tutorial

Este blog acompanha o laboratório da Unidade 3 do Microservices March 2022 – Padrão de segurança de microsserviços no Kubernetes, demonstrando como usar o NGINX e o NGINX Ingress Controller para bloquear injeções de SQL.

Para seguir o tutorial, você precisa de uma máquina com:

  • 2 CPUs ou mais
  • 2 GB de memória livre
  • 20 GB de espaço livre em disco
  • Conexão com a internet
  • Gerenciador de contêineres ou máquinas virtuais, como Docker, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox ou VMware Fusion/Workstation
  • minikube instalado
  • Helm instalado
  • Uma configuração que permita abrir uma janela do navegador. Se isso não for possível, você precisará descobrir como acessar os serviços relevantes por meio de um navegador.

Para aproveitar ao máximo o laboratório e o tutorial, recomendamos que, antes de começar, você:

 

Este tutorial usa estas tecnologias:

As instruções de cada desafio incluem o texto completo dos arquivos YAML usados para configurar os aplicativos. Você também pode copiar o texto do nosso repositório no GitHub. Um link para o GitHub é fornecido junto com o texto de cada arquivo YAML.

Este tutorial inclui quatro desafios:

  1. Implante um cluster e um aplicativo vulnerável
  2. Invada o aplicativo
  3. Use um contêiner sidecar do NGINX para bloquear determinadas solicitações
  4. Configure o NGINX Ingress Controller para filtrar solicitações

Desafio 1: implantar um cluster e um aplicativo vulnerável

Neste desafio, você vai implantar um cluster do minikube e instalar o Podinfo como um aplicativo de exemplo que apresenta vulnerabilidades de segurança.

Crie um cluster do Minikube

Implante um cluster minikube. Após alguns segundos, uma mensagem confirmará que a implantação foi bem-sucedida.

$ minikube start 
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Instale o aplicativo vulnerável

Aqui, você implanta um aplicativo simples de comércio eletrônico composto por dois microsserviços:

  • Um banco de dados MariaDB
  • Um microsserviço PHP que se conecta ao banco de dados e recupera dados

Siga estas etapas:

  1. Usando o editor de texto de sua preferência, crie um arquivo YAML chamado 1-app.yaml com o seguinte conteúdo (ou copie do GitHub).
apiVersion: apps/v1 kind: Deployment 
metadata: 
  name: app 
spec: 
  selector: 
    matchLabels: 
      app: app 
  template: 
    metadata: 
      labels: 
        app: app 
    spec: 
      containers: 
        - name: app 
          image: f5devcentral/microservicesmarch:1.0.3 
          ports: 
            - containerPort: 80 
          env: 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 
            - name: DATABASE_HOSTNAME 
              value: db.default.svc.cluster.local 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: app 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
      nodePort: 30001 
  selector: 
    app: app 
  type: NodePort 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: db 
spec: 
  selector: 
    matchLabels: 
      app: db 
  template: 
    metadata: 
      labels: 
        app: db 
    spec: 
      containers: 
        - name: db 
          image: mariadb:10.3.32-focal 
          ports: 
            - containerPort: 3306 
          env: 
            - name: MYSQL_ROOT_PASSWORD 
              value: root 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 

--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: db 
spec: 
  ports: 
    - port: 3306 
      targetPort: 3306 
  selector: 
    app: db 
  1. Implante o aplicativo e a API:
$ kubectl apply -f 1-app.yaml deployment.apps/app created 
service/app created 
deployment.apps/db created 
service/db created 
  1. Confirme se os pods do Podinfo foram implantados, conforme indicado pelo valor Running na coluna STATUS. A implantação completa pode levar de 30 a 40 segundos. Portanto, aguarde até que o status de ambos os pods seja Running antes de prosseguir para a próxima etapa (execute o comando novamente, se necessário).
$ kubectl get podsNAME                  READY   STATUS    RESTARTS   AGE 
app-d65d9b879-b65f2   1/1     Running   0          37s 
db-7bbcdc75c-q2kt5    1/1     Running   0          37s 
  1. Abra o aplicativo no seu navegador:
$ minikube service app|-----------|------|-------------|--------------| 
| NAMESPACE | NAME | TARGET PORT |     URL      | 
|-----------|------|-------------|--------------| 
| default   | app  |             | No node port | 
|-----------|------|-------------|--------------| 
😿  service default/app has no node port 
🏃  Starting tunnel for service app. 
|-----------|------|-------------|------------------------| 
| NAMESPACE | NAME | TARGET PORT |          URL           | 
|-----------|------|-------------|------------------------| 
| default   | app  |             | http://127.0.0.1:55446 | 
|-----------|------|-------------|------------------------| 
🎉  Opening service default/app in default browser... 

Desafio 2: Hackeie o aplicativo

O aplicativo de exemplo é bastante básico. Ele inclui uma página inicial com uma lista de itens (por exemplo, travesseiros) e um conjunto de páginas de produtos com detalhes como descrição e preço. Os dados são armazenados no banco de dados MariaDB. Sempre que uma página é solicitada, uma consulta SQL é executada no banco de dados.

  • Para a página inicial, todos os itens do banco de dados são recuperados.
  • Para uma página de produto, o item é obtido pelo ID.

Ao abrir a página do produto travesseiros, você pode notar que a URL termina em /product/1. O número 1 é o ID do produto. Para impedir a inserção direta de código malicioso na consulta SQL, é recomendável sanitizar a entrada do usuário antes de encaminhar as solicitações aos serviços de back-end. Mas e se o aplicativo não estiver configurado corretamente e a entrada não passar por escape antes de ser inserida na consulta SQL ao banco de dados?

Exploração 1

Para descobrir se o aplicativo está escapando corretamente a entrada, faça um teste simples alterando o ID para um que não exista no banco de dados.

Altere manualmente o último elemento da URL de 1 para -1. A mensagem de erro Invalid product id "-1" indica que o ID do produto não está sendo escapado – em vez disso, a string é inserida diretamente na consulta. Isso não é nada bom, a menos que você seja um hacker!

Suponha que a consulta ao banco de dados seja algo como:

SELECT * FROM some_table WHERE id = "1"

Para explorar a vulnerabilidade causada pela falta de escape da entrada, substitua 1 por -1" <malicious_query> -- // de modo que:

  • As aspas (") após -1 concluem a primeira consulta.
  • Você pode adicionar sua própria consulta maliciosa após as aspas.
  • A sequência -- // descarta o restante da consulta.

Assim, por exemplo, se você alterar o elemento final da URL para ‑1" ou 1 -- //, a consulta será compilada como:

SELECT * FROM some_table WHERE id = "-1" OR 1 -- //" 
                                     -------------- 
                                     ^ injected ^ 

Isso seleciona todas as linhas do banco de dados, o que é útil em um ataque. Para descobrir se esse é o caso, altere o final da URL para ‑1". A mensagem de erro resultante fornece informações mais úteis sobre o banco de dados:

Fatal error: Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '"-1""' at line 1 in /var/www/html/product.php:23 Stack trace: #0 /var/www/html/product.php(23): mysqli->query('SELECT * FROM p...') #1 {main} thrown in /var/www/html/product.php on line 23

Agora, você pode começar a manipular o código injetado na tentativa de ordenar os resultados do banco de dados por ID:

-1" OR 1 ORDER BY id DESC -- //

O resultado é a página do produto correspondente ao último item do banco de dados.

Exploração 2

Forçar o banco de dados a ordenar os resultados é interessante, mas não é especialmente útil se o seu objetivo é invadir o sistema. Tentar extrair nomes de usuário e senhas do banco de dados vale muito mais a pena.

É seguro presumir que há uma tabela de usuários no banco de dados contendo nomes de usuário e senhas. Mas como ampliar seu acesso da tabela de produtos para a tabela de usuários?

A resposta é injetar um código como este:

-1" UNION SELECT * FROM users -- //

onde

  • ‑1" força o retorno de um conjunto vazio da primeira consulta.
  • UNION força a união de duas tabelas do banco de dados — neste caso, products e users — permitindo obter informações (senhas) que não estão na tabela original (products).
  • SELECT * FROM users seleciona todas as linhas da tabela users.
  • A sequência -- // descarta tudo o que vem depois da consulta maliciosa.

Ao modificar a URL para que termine com o código injetado, você recebe uma nova mensagem de erro:

Fatal error: Uncaught mysqli_sql_exception: The used SELECT statements have a different number of columns in /var/www/html/product.php:23 Stack trace: #0 /var/www/html/product.php(23): mysqli->query('SELECT * FROM p...') #1 {main} thrown in /var/www/html/product.php on line 23

Essa mensagem revela que as tabelas products e users não têm o mesmo número de colunas, portanto a instrução UNION não pode ser executada. No entanto, você pode descobrir o número de colunas por tentativa e erro, adicionando uma coluna (nome de campo) por vez como parâmetro da instrução SELECT. Um bom palpite para o nome de um campo em uma tabela users é password, então tente isso:

# select 1 column-1" UNION SELECT password FROM users; -- //
# select 2 columns 
-1" UNION SELECT password,password FROM users; -- //
# select 3 columns 
-1" UNION SELECT password,password,password FROM users; -- /
# select 4 columns 
-1" UNION SELECT password,password,password,password FROM users; -- //
# select 5 columns
-1" UNION SELECT password,password,password,password,password FROM users; -- //

A última consulta é bem-sucedida (indicando que há cinco colunas na tabela users) e você vê a senha de um usuário:

Neste ponto, você não sabe qual nome de usuário corresponde a essa senha. Mas, sabendo o número de colunas da tabela users, você pode usar os mesmos tipos de consulta usados anteriormente para revelar essa informação. Suponha que o nome do campo relevante seja username. E isso acaba se confirmando – a consulta a seguir revela tanto o nome de usuário quanto a senha da tabela users. O que é ótimo – a menos que esse aplicativo esteja hospedado na sua infraestrutura!

-1" UNION SELECT username,username,password,password,username FROM users where id=1 -- //

Desafio 3: use um contêiner sidecar do NGINX para bloquear determinadas requisições

O desenvolvedor do aplicativo da loja online obviamente precisa prestar mais atenção à sanitização das entradas dos usuários (por exemplo, usando consultas parametrizadas), mas, como engenheiro de Kubernetes, você também pode ajudar a evitar a injeção de SQL impedindo que o ataque chegue ao aplicativo. Dessa forma, o fato de o aplicativo ser vulnerável não é tão relevante.

Há muitas maneiras de proteger seus aplicativos. No restante deste laboratório, vamos nos concentrar em duas:

Implante o NGINX Open Source como um sidecar

  1. Crie um arquivo YAML chamado 2-app-sidecar.yaml com o seguinte conteúdo (ou copie do GitHub). Os aspectos importantes da configuração incluem:
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: app 
spec: 
  selector: 
    matchLabels: 
      app: app 
  template: 
    metadata: 
      labels: 
        app: app 
    spec: 
      containers: 
        - name: app 
          image: f5devcentral/microservicesmarch:1.0.3 
          ports: 
            - containerPort: 80 
          env: 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 
            - name: DATABASE_HOSTNAME 
              value: db.default.svc.cluster.local 
        - name: proxy # <-- sidecar 
          image: "nginx" 
          ports: 
            - containerPort: 8080 
          volumeMounts: 
            - mountPath: /etc/nginx 
              name: nginx-config 
      volumes: 
        - name: nginx-config 
          configMap: 
            name: sidecar 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: app 
spec: 
  ports: 
    - port: 80 
      targetPort: 8080 # <-- the traffic is routed to the proxy 
      nodePort: 30001 
  selector: 
    app: app 
  type: NodePort 
--- 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: sidecar 
data: 
  nginx.conf: |- 
    events {} 
    http { 
      server { 
        listen 8080 default_server; 
        listen [::]:8080 default_server; 

        location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { 
            deny all; 
        } 

        location / { 
            proxy_pass http://localhost:80/; 
        } 
      } 
    } 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: db 
spec: 
  selector: 
    matchLabels: 
      app: db 
  template: 
    metadata: 
      labels: 
        app: db 
    spec: 
      containers: 
        - name: db 
          image: mariadb:10.3.32-focal 
          ports: 
            - containerPort: 3306 
          env: 
            - name: MYSQL_ROOT_PASSWORD 
              value: root 
            - name: MYSQL_USER 
              value: dan 
            - name: MYSQL_PASSWORD 
              value: dan 
            - name: MYSQL_DATABASE 
              value: sqlitraining 

--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: db 
spec: 
  ports: 
    - port: 3306 
      targetPort: 3306 
  selector: 
    app: db
  • Um contêiner sidecar executando o NGINX Open Source é iniciado na porta 8080.
  • O NGINX encaminha todo o tráfego para o aplicativo.
  • Qualquer solicitação que contenha (entre outras cadeias de caracteres) SELECT ou UNION será negada (consulte o primeiro bloco location na seção ConfigMap).
  • O serviço do aplicativo encaminha primeiro todo o tráfego para o contêiner NGINX.
  1. Implante o sidecar:
$ kubectl apply -f 2-app-sidecar.yaml deployment.apps/app configured 
service/app configured 
configmap/sidecar created 
deployment.apps/db unchanged 
service/db unchanged 

Teste o sidecar como um filtro

Teste se o sidecar está filtrando o tráfego voltando ao aplicativo e tentando novamente a injeção de SQL. O NGINX bloqueia a solicitação antes que ela chegue ao aplicativo!

-1" UNION SELECT username,username,password,password,username FROM users where id=1 -- // 

Desafio 4: configure o NGINX Ingress Controller para filtrar requisições

Proteger seu aplicativo como no Desafio 3 é uma experiência educacional interessante, mas não recomendamos essa abordagem em produção porque:

  • Não é uma solução de segurança completa.
  • Não é escalável (não é possível aplicar facilmente essa proteção a vários aplicativos).
  • Atualizá-la é complicado e ineficiente.

Uma solução muito melhor é usar o NGINX Ingress Controller para estender a mesma proteção a todos os seus aplicativos! Os controladores de Ingress podem ser usados para centralizar todos os tipos de recursos de segurança, desde o bloqueio de solicitações, como faz um firewall de aplicações web (WAF), até a autenticação e a autorização.

Neste desafio, você implanta o NGINX Ingress Controller, configura o roteamento de tráfego, e verifica se o filtro bloqueia a injeção de SQL.

Implantar o NGINX Ingress Controller

A maneira mais rápida de instalar o NGINX Ingress Controller é usando o Helm.

  1. Adicione o repositório do NGINX ao Helm:
$ helm repo add nginx-stable https://helm.nginx.com/stable 
  1. Baixe e instale o NGINX Ingress Controller baseado no NGINX Open Source, mantido pela F5 NGINX. Observe o parâmetro enableSnippets=true: os snippets são usados para configurar o NGINX para bloquear a injeção de SQL. A última linha da saída confirma que a instalação foi bem-sucedida.
$ helm install main nginx-stable/nginx-ingress \ --set controller.watchIngressWithoutClass=true --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30005 \ --set controller.enableSnippets=true
NAME: main  
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY  
NAMESPACE: default  
STATUS: deployed  
REVISION: 1  
TEST SUITE: None  
NOTES: The NGINX Ingress Controller has been installed.  
  1. Confirme se o pod do NGINX Ingress Controller foi implantado, conforme indicado pelo valor Running na coluna STATUS.
$ kubectl get pods   NAME                                READY   STATUS  ...
main-nginx-ingress-779b74bb8b-mtdkr 1/1     Running ...

      ... RESTARTS   AGE 
      ... 0          18s

Direcione o tráfego para seu aplicativo

  1. Crie um arquivo YAML chamado 3-ingress.yaml com o conteúdo a seguir (ou copie-o do GitHub). Ele define o manifesto do Ingress necessário para encaminhar o tráfego ao aplicativo (desta vez, sem passar pelo proxy sidecar). Observe o bloco annotations: em que um snippet é usado para personalizar a configuração do NGINX Ingress Controller com o mesmo bloco location da definição do ConfigMap no Desafio 3: ele rejeita qualquer solicitação que contenha (entre outras cadeias de caracteres) SELECT ou UNION.
apiVersion: v1 kind: Service 
metadata: 
  name: app-without-sidecar 
spec: 
  ports: 
    - port: 80 
      targetPort: 80 
  selector: 
    app: app 
--- 
apiVersion: networking.k8s.io/v1 
kind: Ingress 
metadata: 
  name: entry 
  annotations: 
    nginx.org/server-snippets: | 
      location ~* "(\'|\")(.*)(drop|insert|md5|select|union)" { 
          deny all; 
      } 
spec: 
  ingressClassName: nginx 
  rules: 
    - host: "example.com" 
      http: 
        paths: 
          - backend: 
              service: 
                name: app-without-sidecar 
                port: 
                  number: 80 
            path: / 
            pathType: Prefix 
  1. Implante o recurso Ingress:
$ kubectl apply -f 3-ingress.yaml service/app-without-sidecar created 
ingress.networking.k8s.io/entry created 

Verificar a operação do filtro

  1. Inicie um contêiner descartável do BusyBox para enviar uma solicitação ao pod do NGINX Ingress Controller usando o nome de host correto.
$ kubectl run -ti --rm=true busybox --image=busybox$ wget --header="Host: example.com" -qO- main-nginx-ingress 
<!DOCTYPE html> 
<html lang="en"> 

<head> 
# ...
  1. Tente realizar a injeção de SQL. O código de status 403 Proibido confirma que o NGINX bloqueia o ataque! 
$ wget --header="Host: example.com" -qO- 'main-nginx-ingress/product/-1"%20UNION%20SELECT%20username,username,password,password,username%20FROM%20users%20where%2 0id=1%20--%20//' 
wget: server returned error: HTTP/1.1 403 Forbidden 

Próximos passos

O Kubernetes não é seguro por padrão. Um controlador Ingress pode mitigar vulnerabilidades de injeção de SQL (e muitas outras). No entanto, lembre-se de que o tipo de funcionalidade semelhante a um WAF que você acabou de implementar com o NGINX Ingress Controller não substitui um WAF de verdade, nem dispensa uma arquitetura segura para os aplicativos. Um hacker experiente ainda pode fazer o ataque com UNION funcionar com algumas pequenas alterações no código. Para saber mais sobre esse assunto, consulte Guia de injeção de SQL (SQLi) para pentesters.

Dito isso, um controlador de Ingress ainda é uma ferramenta poderosa para centralizar a maior parte da sua segurança, proporcionando mais eficiência e proteção, inclusive em casos de uso de autenticação e autorização centralizadas (mTLS, logon único), além de permitir o uso de um WAF robusto como o F5 NGINX App Protect WAF.

A complexidade dos seus aplicativos e da sua arquitetura pode exigir um controle mais granular. Se a sua organização precisar de confiança zero (Zero Trust) e criptografia de ponta a ponta, considere uma malha de serviços sempre gratuita, como o F5 NGINX Service Mesh para controlar a comunicação entre os serviços no cluster do Kubernetes (tráfego leste-oeste). Abordamos as malhas de serviços na Unidade 4, Estratégias avançadas de implantação do Kubernetes.

Para obter detalhes sobre como adquirir e implantar o NGINX Open Source, acesse nginx.org.

Para experimentar o NGINX Ingress Controller baseado no NGINX Plus com NGINX App Protect, inicie sua avaliação gratuita de 30 dias hoje mesmo ou entre em contato conosco para conversar sobre seus casos de uso.

Para experimentar o NGINX Ingress Controller baseado no NGINX Open Source, consulte NGINX Ingress Controller Releases em nosso repositório do GitHub ou baixe um contêiner pré-compilado do DockerHub.


Compartilhar

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.