Tenho uma conexão SSL com um servidor (owner-api.teslamotors.com) que trava com wget, curl ou openssl s_client. Estou mostrando a versão curl, pois ela fornece a maioria das mensagens de depuração:
# curl -iv https://owner-api.teslamotors.com
* Trying 18.203.70.200:443...
* Connected to owner-api.teslamotors.com (18.203.70.200) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
Lá ele trava depois de ClientHello. Conexão TCP estabelecida com sucesso (também confirmada com telnet/nc). Outra conexão de rede, incluindo qualquer conexão SSL que eu tentei, funciona. Exceto owner-api.teslamotors.com:443.
Achei esta postagem falando sobre MTU e parecia absurdo. Mas reduzi o MTU do servidor e funcionou! Funciona com qualquer MTU <= 1420.
O servidor se conecta usando Ethernet (MTU 1500) a um roteador Mikrotik e de lá a conexão passa por um túnel WireGuard (MTU 1420). Estou ciente de que isso pode não ser o ideal, pois qualquer pacote IP do servidor >1420 precisará ser fragmentado. No entanto, isso é agnóstico de qualquer protocolo L4. SSL sobre TCP não deve se importar com fragmentação e MTU. No entanto, este host se importa.
Executei uma captura de pacotes na caixa Mikrotik e o tráfego não lista nada de anormal para mim:
O típico handshake TCP Num 1-3, depois ClientHello (Num 4-5) e ServerHello (Num 6-7). Nenhum tamanho de pacote chega perto do MTU e nenhuma outra mensagem ICMP que indicaria problemas com fragmentação etc.
Por comentário, aqui está a saída do tracepath:
# tracepath owner-api.teslamotors.com
1?: [LOCALHOST] pmtu 1500
1: XX.XX.56.210 0.410ms
1: XX.XX.56.210 0.198ms
2: XX.XX.56.210 0.138ms pmtu 1420
2: XX.XX.56.185 151.394ms
3: no reply
4: 100.100.200.1 157.220ms
5: 10.75.0.193 154.068ms
6: 10.75.2.53 161.950ms asymm 7
7: decix1.amazon.com 152.107ms asymm 8
8: decix2.amazon.com 153.068ms
9: no reply
[...]
Estou realmente perdido, o que diabos está acontecendo aqui?
Por que essa conexão SSL falha?
O cerne do problema é que o servidor não sabe que tais pacotes precisam ser fragmentados.
Há pacotes grandes – como a
Certificate
mensagem TLS do servidor 4 – mas sua captura não os está vendo porque eles são maiores que sua MTU, então eles nunca chegam ao seu lado. Esse é literalmente o problema; se esses pacotes alcançassem sua interface de rede (de forma que se tornassem visíveis em uma captura de pacotes), então a conexão não travaria.A captura precisa ser feita na extremidade "upstream" do túnel, especificamente na interface de entrada que está um passo antes da interface 'low MTU'. Então, se o caminho for "internet → server eth0 ⇒ server wg0 → client wg-foo ⇒ client ether1", então os pacotes grandes serão visíveis no "server eth0", mas não caberão no "server wg0". Capturar no wg0 não lhe daria nada, mas capturar no eth0 provavelmente mostraria uma série de:
(Observe que o descarregamento de recebimento de hardware pode gerar resultados confusos, pois sua placa de rede Ethernet pode aglutinar segmentos em um superpacote, por exemplo, ao capturar no próprio host final. Se você vir pacotes com mais de 2 KB de tamanho, pode ser necessário fazer isso
ethtool -K eth0 gso off gro off
durante a captura.)Durante o handshake TCP, o cliente (ambos os pares, na verdade) declara um MSS TCP – tamanho máximo do segmento TCP – que ele pode receber. Como o cliente geralmente tem memória infinita hoje em dia 1 e não está limitado a segmentos minúsculos, ele realmente oferece o maior MSS que ele calcula como ótimo para o MTU que ele conhece , a fim de evitar a necessidade de fragmentação em nível de IP.
Por exemplo, se a MTU da sua interface Ethernet for 1500, seu sistema operacional poderá oferecer um MSS de 1460, que se encaixa exatamente na carga útil do IP (assumindo uma sobrecarga de IPv4 de 20 e uma sobrecarga de TCP de 20 novamente, no caso mais simples).
Portanto, reduzir a MTU da interface de rede do cliente fará com que ele declare um tamanho de segmento TCP aceitável menor antecipadamente, o que faz com que o servidor sempre envie pacotes IP menores (ou seja, fique abaixo do limite no qual a fragmentação seria necessária), como se você tivesse reduzido a MTU do servidor.
Com o MTU padrão de 1500, enquanto isso, o servidor enviará grandes segmentos em grandes pacotes IP, até receber um ICMP "Fragmentação necessária" do gateway do seu ISP (aquele que tem o link de MTU baixo em direção a você e não consegue encaminhar esses pacotes para você); então o servidor notará o novo PMTU em direção a você e começará a enviar esses segmentos fragmentados no nível de IP. 5
Mas se algum firewall impedir que esse erro ICMP chegue ao servidor, isso não acontecerá e o servidor tentará enviar para sempre esse segmento TCP no mesmo pacote IP grande. (Ou, se o servidor estiver atrás de um certo tipo 2 de firewall que remonta e fragmenta novamente todos os pacotes IP que passam por ele, então ele pode estar fragmentando o pacote corretamente, mas o firewall pode estar desfazendo todo o seu trabalho.)
Gateways, como o Linux com nftables/iptables, geralmente têm o recurso de corrigir o MSS anunciado de handshakes TCP que passam por eles para se adequar à MTU que o gateway conhece, por exemplo, quando o cliente está em uma Ethernet de MTU de 1500 bytes, mas o gateway está prestes a encaminhar o pacote por um túnel PPPoE de MTU de 1420 bytes:
Se meu entendimento estiver correto, o TCP precisa se preocupar com o MTU, porque depender da fragmentação de IP reduz a eficiência das retransmissões de TCP – se até mesmo um único fragmento for "perdido", o pacote IP inteiro será "perdido" e nada dele será entregue ao protocolo da camada superior.
Por exemplo, se o TCP enviasse um segmento de 64k que foi fragmentado em 45 datagramas IP e um deles fosse perdido, todos eles precisariam ser retransmitidos após o ICMP "Tempo de remontagem excedido". (Isso pressupõe que a fragmentação funcione, o que, como você vê, às vezes não funciona. 3 )
Enquanto isso, com os mesmos 64k de dados divididos em segmentos TCP que cabem dentro do MTU IP, os outros 44 pacotes IP ainda seriam entregues à camada TCP do destinatário e submetidos ao SACK, e somente o perdido teria que ser retransmitido (o que eu acho que pode até acontecer ~imediatamente após o servidor receber um SACK que indica um buraco, em vez de um longo tempo limite de remontagem).
4 As mensagens de 'Certificado' eram visíveis em texto simples com TLSv1.2, mas são criptografadas com TLSv1.3, então uma captura as verá apenas como 'Dados do Aplicativo'.
1 Ou pelo menos é o que a maioria dos desenvolvedores supõe.
2 Como o Untangle em seu modo padrão de "brouting".
3 Também conhecido como "buraco negro PMTUD". Veja, por exemplo, o post do blog Cloudflare nº 1 , nº 2 e nº 3 para uma situação em que isso acontece por motivos diferentes de um ICMP de bloqueio geral do administrador de sistema.
5 Na verdade, não sei se ele fragmenta os mesmos segmentos no nível IP ou se reduz seu TCP MSS para essa conexão também. Pode ser o último.
Conforme explicado na outra resposta, o motivo do travamento é porque a resposta do servidor é maior que a MTU e o erro ICMP não está chegando ao servidor.
O motivo pelo qual isso só está acontecendo neste servidor é porque ele é o único que está enviando uma resposta grande neste momento.
Se eu enrolar https://owner-api.teslamotors.com
com 18.203.70.200 o endereço IP para o qual ele resolveu owner-api.teslamotors.com (após três CNAMEs, terminando em um host AWS, você provavelmente receberá um diferente) e 203.0.113.16 como o cliente.
Note que o Server Hello é um único pacote TCP de 2958 bytes. Isso claramente não vai caber no MTU.
O Wireshark identifica nele um
TLSv1.3 Record Layer: Handshake Protocol: Server Hello
de 122 bytes, seguido por umTLSv1.3 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec
de 6 bytes, o que não identifica realmente o que está acontecendo.Definindo
--tls-max 1.2
obtemos dados mais legíveis:Ainda recebemos os 2958 bytes do Server hello, mas agora ele não está criptografado, então podemos "ler" que ele contém um certificado:
O pacote com número 4478 é na verdade construído a partir de dois pacotes, começando no anterior:
e seu conteúdo é:
seguido por um
TLSv1.2 Record Layer: Handshake Protocol: Server Hello Done
que tem apenas 9 bytes.Então, o motivo de estar falhando apenas neste servidor é que ele tenta enviar um único pacote TCP de quase 3000 bytes (incorporando um certificado de 3010 bytes), o que não está acontecendo em outros sites que você tentou (no entanto, haverá mais que falharão. Não descarte esses erros ICMP e use uma MTU correspondente, se possível).