BLOG | NGINX

Conheça a rede e criptografia QUIC no NGINX

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Robert Haynes Miniatura
Robert Haynes
Publicado em 19 de abril de 2023

A primeira menção ao QUIC e ao HTTP/3 no blog do NGINX foi há quatro anos (!) e, assim como você, agora estamos ansiosos pela iminente fusão da nossa implementação do QUIC na ramificação principal do NGINX Open Source. Dada a longa gestação, é compreensível que você não tenha pensado muito no QUIC.

Neste ponto, no entanto, como desenvolvedor ou administrador do site, você precisa estar ciente de como o QUIC transfere a responsabilidade por alguns detalhes de rede do sistema operacional para o NGINX (e todos os aplicativos HTTP). Mesmo que networking não seja sua praia, adotar o QUIC significa que se preocupar com a rede agora é (pelo menos um pouco) parte do seu trabalho.

Nesta postagem, nos aprofundamos nos principais conceitos de rede e criptografia usados no QUIC, simplificando alguns detalhes e omitindo informações não essenciais em busca de clareza. Embora algumas nuances possam ser perdidas no processo, nossa intenção é fornecer informações suficientes para que você adote o QUIC de forma eficaz em seu ambiente, ou pelo menos uma base sobre a qual construir seu conhecimento.

Se o QUIC é uma novidade para você, recomendamos que você leia primeiro uma de nossas postagens anteriores e assista ao nosso vídeo de visão geral.

Para uma explicação mais detalhada e completa do QUIC, recomendamos o excelente documento Manageability of the QUIC Transport Protocol do grupo de trabalho QUIC da IETC, juntamente com os materiais adicionais vinculados ao longo deste documento.

Por que você deve se importar com redes e criptografia no QUIC?

Os detalhes obscuros da conexão de rede entre clientes e NGINX não foram particularmente relevantes para a maioria dos usuários até agora. Afinal, com HTTP/1. x e HTTP/2 o sistema operacional cuida da configuração da conexão Transmission Control Protocol (TCP) entre clientes e NGINX. O NGINX simplesmente usa a conexão uma vez que ela é estabelecida.

Com o QUIC, no entanto, a responsabilidade pela criação, validação e gerenciamento de conexões muda do sistema operacional subjacente para o NGINX. Em vez de receber uma conexão TCP estabelecida, o NGINX agora obtém um fluxo de datagramas do User Datagram Protocol (UDP), que ele deve analisar em conexões e fluxos de clientes. O NGINX agora também é responsável por lidar com perda de pacotes, reinicializações de conexão e controle de congestionamento.

Além disso, o QUIC combina iniciação de conexão, negociação de versão e troca de chave de criptografia em uma única operação de estabelecimento de conexão. E embora a criptografia TLS seja tratada de maneira amplamente semelhante para QUIC+HTTP/3 e TCP+HTTP/1+2, há diferenças que podem ser significativas para dispositivos downstream, como balanceadores de carga de Camada 4, firewalls e dispositivos de segurança.

No final das contas, o efeito geral dessas mudanças é uma experiência mais segura, rápida e confiável para os usuários, com muito poucas alterações na configuração ou nas operações do NGINX. Os administradores do NGINX, no entanto, precisam entender pelo menos um pouco do que está acontecendo com o QUIC e o NGINX, mesmo que seja apenas para manter seu tempo médio de inocência o mais curto possível em caso de problemas.

(Vale a pena notar que, embora esta postagem se concentre em operações HTTP porque o HTTP/3 requer QUIC, o QUIC também pode ser usado para outros protocolos. Um bom exemplo é o DNS sobre QUIC, conforme definido no RFC 9250 , DNS sobre conexões QUIC dedicadas .)

Com essa introdução feita, vamos mergulhar em alguns detalhes específicos da rede QUIC.

TCP versus UDP

O QUIC introduz uma mudança significativa no protocolo de rede subjacente usado para transmitir dados de aplicativos HTTP entre um cliente e um servidor.

Como mencionado, o TCP sempre foi o protocolo para transmissão de dados de aplicativos web HTTP. O TCP foi projetado para fornecer dados de forma confiável através de uma rede IP. Ele tem um mecanismo bem definido e compreendido para estabelecer conexões e confirmar o recebimento de dados, juntamente com uma variedade de algoritmos e técnicas para gerenciar a perda e o atraso de pacotes que são comuns em redes não confiáveis e congestionadas.

Embora o TCP forneça transporte confiável, há compensações em termos de desempenho e latência. Além disso, a criptografia de dados não é incorporada ao TCP e deve ser implementada separadamente. Também tem sido difícil melhorar ou estender o TCP em face das mudanças nos padrões de tráfego HTTP – como o processamento do TCP é realizado no kernel do Linux, quaisquer alterações devem ser projetadas e testadas cuidadosamente para evitar efeitos inesperados no desempenho e na estabilidade geral do sistema.

Outro problema é que, em muitos cenários, o tráfego HTTP entre cliente e servidor passa por vários dispositivos de processamento TCP, como firewalls ou balanceadores de carga (conhecidos coletivamente como “middleboxes”), que podem ser lentos para implementar mudanças nos padrões TCP.

O QUIC usa UDP como protocolo de transporte. O UDP foi projetado para transmitir dados através de uma rede IP como o TCP, mas ele intencionalmente descarta o estabelecimento de conexão e a entrega confiável. Essa ausência de sobrecarga torna o UDP adequado para muitas aplicações em que eficiência e velocidade são mais importantes que confiabilidade.

Para a maioria dos aplicativos web, no entanto, a entrega confiável de dados é essencial. Como a camada de transporte UDP subjacente não fornece entrega confiável de dados, essas funções precisam ser fornecidas pelo QUIC (ou pelo próprio aplicativo). Felizmente, o QUIC tem algumas vantagens sobre o TCP nesse aspecto:

  • O processamento QUIC é executado no espaço do usuário Linux, onde problemas com uma operação específica representam menos riscos para o sistema geral. Isso torna o desenvolvimento rápido de novos recursos mais viável.
  • Os “middleboxes” mencionados acima geralmente fazem processamento mínimo do tráfego UDP e, portanto, não restringem melhorias ao protocolo QUIC.

Uma anatomia simplificada da rede QUIC

Os fluxos QUIC são objetos lógicos que contêm solicitações ou respostas HTTP/3 (ou quaisquer outros dados do aplicativo). Para transmissão entre pontos de extremidade da rede, eles são encapsulados dentro de várias camadas lógicas, conforme ilustrado no diagrama.

Figura 1. Anatomia de um fluxo QUIC

Começando de fora para dentro, as camadas e objetos lógicos são:

  • Datagrama UDP – Contém um cabeçalho especificando as portas de origem e destino (junto com dados de comprimento e soma de verificação), seguido por um ou mais pacotes QUIC. O datagrama é a unidade de informação transmitida do cliente para o servidor através da rede.
  • Pacote QUIC – Contém um cabeçalho QUIC e um ou mais quadros QUIC.
  • Cabeçalho QUIC – Contém metadados sobre o pacote. Existem dois tipos de cabeçalho:

    • O cabeçalho longo, usado ao estabelecer a conexão.
    • O cabeçalho curto, usado depois que a conexão é estabelecida. Ele contém (entre outros dados) o ID da conexão, o número do pacote e a fase da chave (usada para rastrear quais chaves foram usadas para criptografar o pacote, em suporte à rotação de chaves). Os números de pacotes são únicos (e sempre aumentam) para uma conexão e fase-chave específicas.
  • Quadro – Contém o tipo, ID do fluxo, deslocamento e dados do fluxo. Os dados de fluxo são distribuídos em vários quadros, mas podem ser reunidos usando o ID de conexão, o ID de fluxo e o deslocamento, que são usados para apresentar os blocos de dados na ordem correta.
  • Fluxo – Um fluxo unidirecional ou bidirecional de dados dentro de uma única conexão QUIC. Cada conexão QUIC pode suportar múltiplos fluxos independentes, cada um com seu próprio ID de fluxo. Se um pacote QUIC contendo alguns fluxos for perdido, isso não afeta o progresso de nenhum fluxo não contido no pacote ausente (isso é crítico para evitar o bloqueio de cabeça de linha experimentado pelo HTTP/2). Os fluxos podem ser bidirecionais e criados por qualquer ponto de extremidade.

Estabelecimento de conexão

O conhecido handshake triplo SYN / SYN-ACK / ACK estabelece uma conexão TCP:

Diagrama mostrando as três mensagens trocadas entre cliente e servidor no handshake para estabelecer uma conexão TCP
Figura 2. O handshake triplo que estabelece uma conexão TCP

Estabelecer uma conexão QUIC envolve etapas semelhantes, mas é mais eficiente. Ele também incorpora a validação de endereço na configuração da conexão como parte do handshake criptográfico. A validação de endereço protege contra ataques de amplificação de tráfego, nos quais um invasor envia ao servidor um pacote com informações de endereço de origem falsificadas para a vítima pretendida do ataque. O invasor espera que o servidor gere mais pacotes ou pacotes maiores para a vítima do que ele mesmo consegue gerar, resultando em uma quantidade enorme de tráfego. (Para mais detalhes, consulte a Seção 8 do RFC 9000, QUIC: Um transporte multiplexado e seguro baseado em UDP .)

Como parte do estabelecimento da conexão, o cliente e o servidor fornecem IDs de conexão independentes que são codificados no cabeçalho QUIC, fornecendo uma identificação simples da conexão, independente do endereço IP de origem do cliente.

Entretanto, como o estabelecimento inicial de uma conexão QUIC também inclui operações para troca de chaves de criptografia TLS, é mais caro computacionalmente para o servidor do que a simples resposta SYN-ACK que ele gera durante o estabelecimento de uma conexão TCP. Ele também cria um vetor potencial para ataques de negação de serviço distribuído (DDoS) , porque o endereço IP do cliente não é validado antes que as operações de troca de chaves ocorram.

Mas você pode configurar o NGINX para validar o endereço IP do cliente antes que operações criptográficas complexas comecem, definindo a diretiva quic_retry como on . Nesse caso, o NGINX envia ao cliente um pacote de nova tentativa contendo um token, que o cliente deve incluir nos pacotes de configuração de conexão.

Diagrama mostrando o handshake para estabelecer uma conexão QUIC, sem e com um pacote de repetição
Figura 3. Configuração de conexão QUIC, sem e com um pacote de repetição

Esse mecanismo é um pouco parecido com o handshake TCP de três vias e, fundamentalmente, estabelece que o cliente é o proprietário do endereço IP de origem que está apresentando. Sem essa verificação, servidores QUIC como o NGINX podem ficar vulneráveis a ataques DoS fáceis com endereços IP de origem falsificados. (Outro mecanismo QUIC que atenua tais ataques é o requisito de que todos os pacotes de conexão iniciais devem ser preenchidos com um mínimo de 1200 bytes, tornando seu envio uma operação mais cara.)

Além disso, os pacotes de repetição atenuam um ataque semelhante ao ataque de inundação TCP SYN (em que os recursos do servidor são esgotados por um grande número de handshakes abertos, mas não concluídos, armazenados na memória), codificando detalhes da conexão na ID de conexão que ele envia ao cliente; isso tem o benefício adicional de que nenhuma informação do lado do servidor precisa ser retida, pois as informações de conexão podem ser reconstituídas a partir da ID de conexão e do token posteriormente apresentados pelo cliente. Essa técnica é análoga aos cookies TCP SYN . Além disso, servidores QUIC como o NGINX podem fornecer um token expirado para ser usado em conexões futuras do cliente, para acelerar a retomada da conexão.

O uso de IDs de conexão permite que a conexão seja independente da camada de transporte subjacente, de modo que alterações na rede não precisem causar a interrupção das conexões. Isso é discutido em Gerenciando com elegância as alterações de endereço IP do cliente .

Detecção de Perdas

Com uma conexão estabelecida (e criptografia habilitada, conforme discutido mais adiante ), solicitações e respostas HTTP podem fluir para frente e para trás entre o cliente e o NGINX. Datagramas UDP são enviados e recebidos. No entanto, há muitos fatores que podem causar a perda ou atraso de alguns desses datagramas.

O TCP tem mecanismos complexos para reconhecer a entrega de pacotes, detectar perdas ou atrasos de pacotes e gerenciar a retransmissão de pacotes perdidos, entregando dados devidamente sequenciados e completos para a camada de aplicação. O UDP não possui esse recurso e, portanto, o controle de congestionamento e a detecção de perdas são implementados na camada QUIC.

  • Tanto o cliente quanto o servidor enviam uma confirmação explícita para cada pacote QUIC que recebem (embora os pacotes que contêm apenas quadros de baixa prioridade não sejam confirmados imediatamente).
  • Quando um pacote contendo quadros que exigem entrega confiável não é confirmado após um período de tempo limite definido, ele é considerado perdido.

    Os períodos de tempo limite variam dependendo do que está no pacote – por exemplo, o tempo limite é menor para pacotes que são necessários para estabelecer criptografia e configurar a conexão, porque eles são essenciais para o desempenho do QUIC handshake.

  • Quando um pacote é considerado perdido, os quadros ausentes são retransmitidos em um novo pacote, que tem um novo número de sequência.
  • O destinatário do pacote usa o ID do fluxo e o deslocamento nos pacotes para reunir os dados transmitidos na ordem correta. O número do pacote determina apenas a ordem de envio, não como os pacotes devem ser montados.
  • Como a montagem de dados no receptor é independente da ordem de transmissão, um pacote perdido ou atrasado afeta apenas os fluxos individuais que ele contém, não todos os fluxos na conexão. Isso elimina o problema de bloqueio de linha de frente que afeta HTTP/1. x e HTTP/2 porque os fluxos não fazem parte da camada de transporte.

Uma descrição completa da detecção de perdas está além do escopo deste manual. Consulte RFC 9002 , QUIC Loss Detection and Congestion Control , para obter detalhes sobre os mecanismos para determinar tempos limite e quantos dados não confirmados podem estar em trânsito.

Gerenciando com elegância as alterações de endereço IP do cliente

O endereço IP de um cliente (chamado de endereço IP de origem no contexto de uma sessão de aplicativo) está sujeito a alterações durante a sessão, por exemplo, quando uma VPN ou gateway altera seu endereço público ou um usuário de smartphone sai de um local coberto por WiFi, o que força uma troca para uma rede celular. Além disso, os administradores de rede tradicionalmente definem tempos limite mais baixos para o tráfego UDP do que para conexões TCP, o que resulta em maior probabilidade de revinculação de tradução de endereço de rede (NAT).

O QUIC fornece dois mecanismos para reduzir a interrupção que pode resultar: um cliente pode informar proativamente o servidor que seu endereço vai mudar, e os servidores podem lidar com elegância com uma mudança não planejada no endereço do cliente. Como o ID da conexão permanece consistente durante a transição, os quadros não confirmados podem ser retransmitidos para o novo endereço IP.

Alterações no endereço IP de origem durante sessões QUIC podem representar um problema para balanceadores de carga downstream (ou outros componentes de rede da Camada 4) que usam o endereço IP de origem e a porta para determinar qual servidor upstream deve receber um datagrama UDP específico. Para garantir o gerenciamento correto do tráfego, os provedores de dispositivos de rede da Camada 4 precisarão atualizá-los para lidar com IDs de conexão QUIC. Para saber mais sobre o futuro do balanceamento de carga e QUIC, consulte o rascunho do QUIC‑LB da IETF: Gerando IDs de conexão QUIC roteáveis .

Criptografia

Em Estabelecimento de conexão , aludimos ao fato de que o handshake QUIC inicial faz mais do que simplesmente estabelecer uma conexão. Ao contrário do handshake TLS para TCP, com UDP a troca de chaves e parâmetros de criptografia TLS 1.3 ocorre como parte da conexão inicial. Esse recurso remove várias trocas e permite tempo de ida e volta zero (0‑RTT) quando o cliente retoma uma conexão anterior.

Figura 4. Comparação dos handshakes de criptografia para TCP+TLS/1.3 e QUIC

Além de incorporar o handshake de criptografia ao processo de estabelecimento de conexão, o QUIC criptografa uma porção maior dos metadados do que o TCP+TLS. Mesmo antes da troca de chaves ocorrer, os pacotes de conexão iniciais são criptografados; embora um espião ainda possa obter as chaves, isso exige mais esforço do que com pacotes não criptografados. Isso protege melhor dados como o Indicador de Nome do Servidor (SNI), que é relevante tanto para invasores quanto para possíveis censores estaduais. A Figura 5 ilustra como o QUIC criptografa metadados potencialmente mais sensíveis (em vermelho) do que o TCP+TLS.

Figura 5. QUIC criptografa metadados mais sensíveis do que TCP+TLS

Todos os dados na carga útil do QUIC são criptografados usando TLS 1.3. Há duas vantagens: conjuntos de cifras e algoritmos de hash mais antigos e vulneráveis não são permitidos e mecanismos de troca de chaves de sigilo de encaminhamento (FS) são obrigatórios. O sigilo de encaminhamento impede que um invasor descriptografe os dados, mesmo que ele capture a chave privada e uma cópia do tráfego.

Conexões Low e Zero-RTT reduzem a latência

Reduzir o número de viagens de ida e volta que devem ocorrer entre um cliente e um servidor antes que quaisquer dados do aplicativo possam ser transmitidos melhora o desempenho dos aplicativos, principalmente em redes com maior latência.

O TLS 1.3 introduziu uma única viagem de ida e volta para estabelecer uma conexão criptografada e zero viagens de ida e volta para retomar uma conexão, mas com o TCP isso significa que o handshake tem que ocorrer antes do TLS Client Hello.

Como o QUIC combina operações criptográficas com configuração de conexão, ele fornece um verdadeiro restabelecimento de conexão 0-RTT, onde um cliente pode enviar uma solicitação no primeiro pacote QUIC. Isso reduz a latência eliminando a viagem de ida e volta inicial para estabelecimento da conexão antes da primeira solicitação.

Figura 6. Comparação das mensagens necessárias para restabelecer uma conexão com TCP+TLS versus QUIC

Nesse caso, o cliente envia uma solicitação HTTP criptografada com os parâmetros usados em uma conexão anterior e, para fins de validação de endereço, inclui um token fornecido pelo servidor durante a conexão anterior.

Infelizmente, a retomada da conexão 0-RTT não fornece sigilo de encaminhamento, portanto, a solicitação inicial do cliente não é criptografada com tanta segurança quanto outros tráfegos na troca. Solicitações e respostas além da primeira solicitação são protegidas pelo Forward Secrecy. Talvez o mais problemático seja que a solicitação inicial também seja vulnerável a ataques de repetição , onde um invasor pode capturar a solicitação inicial e reproduzi-la no servidor diversas vezes.

Para muitos aplicativos e sites, a melhoria de desempenho da retomada da conexão 0-RTT supera essas vulnerabilidades potenciais, mas essa é uma decisão que você precisa tomar por si mesmo.

Este recurso é desabilitado por padrão no NGINX. Para habilitá-lo, defina a diretiva ssl_early_data como on .

Migrando de HTTP/1.1 para HTTP/3 com o cabeçalho Alt-Svc

Quase todos os clientes (navegadores em particular) fazem conexões iniciais via TCP/TLS. Se um servidor suportar QUIC+HTTP/3, ele sinaliza esse fato ao cliente retornando uma resposta HTTP/1.1 que inclui o parâmetro h3 no cabeçalho Alt-Svc . O cliente então escolhe se deseja usar QUIC+HTTP/3 ou manter uma versão anterior do HTTP. (A título de curiosidade, o cabeçalho Alt-Svc , definido no RFC 7838 , é anterior ao QUIC e pode ser usado para outros propósitos também.)

Figura 7. Como o cabeçalho Alt-Svc é usado para converter uma conexão de HTTP/1.1 para HTTP/3

O cabeçalho Alt-Svc informa ao cliente que o mesmo serviço está disponível em um host, protocolo ou porta alternativos (ou uma combinação deles). Além disso, os clientes podem ser informados sobre por quanto tempo é seguro assumir que esse serviço continuará disponível.

Alguns exemplos:

Alt-Svc: h3=":443" HTTP/3 está disponível neste servidor na porta 443
Alt-Svc: h3="new.example.com:8443" HTTP/3 está disponível no servidor new.example.com na porta 8443
Alt-Svc: h3=":8443"; ma=600 HTTP/3 está disponível neste servidor na porta 8443 e estará disponível por pelo menos 10 minutos

Embora não seja obrigatório, na maioria dos casos os servidores são configurados para responder a conexões QUIC na mesma porta que o TCP+TLS.

Para configurar o NGINX para incluir o cabeçalho Alt-Svc , use a diretiva add_header . Neste exemplo, a variável $server_port significa que o NGINX aceita conexões QUIC na porta para a qual o cliente enviou sua solicitação TCP+TLS, e 86.400 são 24 horas:

add_header Alt-Svc 'h3=":$server_port"; ma=86400';

Conclusão

Este blog fornece uma introdução simplificada ao QUIC e esperamos que lhe dê uma visão geral suficiente para entender as principais operações de rede e criptografia usadas com o QUIC.

Para uma visão mais abrangente sobre a configuração do NGINX para QUIC + HTTP/3, leia Pacotes binários agora disponíveis para a implementação prévia do NGINX QUIC+HTTP/3 em nosso blog ou assista ao nosso webinar, Pratique o NGINX e o QUIC+HTTP/3 . Para obter detalhes sobre todas as diretivas NGINX para QUIC+HTTP/3 e instruções completas para instalar binários pré-compilados ou compilar a partir do código-fonte, consulte a página da Web NGINX QUIC .


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