Quando uma aplicação funciona através de um proxy e algo dá errado, a primeira coisa que o engenheiro abre é o log do cliente. Lá está escrito algo como connection reset by peer, read timeout ou simplesmente EOF. O problema é que essas linhas quase não dizem nada sobre a causa. Quem resetou a conexão: o proxy, o servidor de destino ou a operadora no caminho entre você e o proxy? A requisição sequer chegou ao proxy? O handshake TLS chegou a ser concluído? A biblioteca do cliente HTTP não sabe disso, então você também não sabe.

Neste artigo vamos descer um nível, até os pacotes. Vamos ver como capturar um dump com tcpdump na máquina cliente, o que exatamente é visível nesse dump ao trabalhar através de um proxy HTTP e SOCKS5, por que em um túnel HTTPS você só verá o CONNECT e registros TLS criptografados, como ler o dump no Wireshark e como, pelas flags RST, FIN, retransmissões e janela zero, determinar de qual lado a conexão foi interrompida. Separadamente, falaremos sobre descriptografar o próprio tráfego através do SSLKEYLOGFILE e sobre como preparar corretamente um dump para entrar em contato com o suporte de um serviço de proxy, como o Proxeon.

Uma ressalva importante: este não é um artigo sobre mitmproxy. Lá se trata de interceptação e substituição de HTTPS no nível da aplicação com um certificado falso. Aqui trabalhamos no nível de pacotes, não substituímos nada nem espionamos tráfego alheio. Nossa tarefa é puramente diagnóstica: entender onde a cadeia cliente - proxy - servidor de destino se rompe.

Introdução: quando os logs do cliente já não bastam

Imagine uma situação típica. Um script em Python opera através de um proxy móvel, processa alguns milhares de requisições por hora, e cerca de dois por cento delas terminam com o erro Connection aborted, RemoteDisconnected. O desenvolvedor adiciona retries, o erro não desaparece. Ele escreve para o suporte do proxy, que pede um exemplo de requisição e o horário. O suporte olha seus logs e responde: do nosso lado está tudo em ordem, a conexão com o servidor de destino era estabelecida. Quem está certo?

Sem um dump essa discussão é infinita. Com um dump ela termina em cinco minutos: fica visível que o proxy respondeu ao CONNECT com código 200, o cliente enviou o ClientHello, e 180 milissegundos depois chegou um RST vindo do proxy. Isso significa que ou o proxy não conseguiu negociar com o servidor de destino, ou o servidor mesmo fechou a conexão. Daí em diante dá para olhar timings e refinar. O principal é que a conversa saiu do campo das suposições para o campo dos fatos.

Os logs do cliente HTTP operam no nível da aplicação. Eles veem o resultado, mas não o processo. Um dump de tráfego mostra o processo: cada pacote com marcação precisa de tempo, direção, flags e tamanho. É justamente por isso que, na operação séria de infraestrutura de proxy, saber capturar e ler um dump é considerado uma habilidade básica, e não exotismo.

O que você vai tirar deste artigo

  • Entendimento de quais dados fisicamente passam pela interface de rede do cliente ao trabalhar através de proxy e o que deles pode ser visto em texto claro.
  • Comandos prontos de tcpdump para gravar dump com filtros, rotação e limite de tamanho.
  • Um conjunto de filtros de exibição do Wireshark que podem ser copiados inteiros.
  • Uma metodologia para distinguir queda do seu lado, do lado do proxy e do lado do servidor de destino.
  • Uma forma segura de descriptografar seu próprio tráfego TLS para depuração.
  • Um checklist de preparação de dump para contato com o suporte.

Fundamentos: o que afinal é visível antes do proxy, dentro do túnel e depois dele

Comecemos pelo esquema. Ao trabalhar através de proxy existem três trechos do caminho, e na máquina cliente você fisicamente só consegue observar o primeiro.

Três zonas de observação

  1. Zona A: cliente - proxy. Esta é a única conexão TCP que passa pela sua interface de rede. É ela que o tcpdump vê na sua máquina. Aqui são visíveis: o handshake TCP com o endereço IP do proxy, o protocolo de serviço do proxy (HTTP CONNECT ou SOCKS5), e depois ou HTTP aberto ou registros TLS criptografados.
  2. Zona B: dentro do túnel. Depois que o proxy responde 200 Connection established, todos os bytes entre você e o servidor de destino são simplesmente repassados adiante. Se o servidor de destino opera por HTTPS, esses bytes são registros TLS. Você vê a estrutura deles (tipo de registro, tamanho, ClientHello com SNI, ServerHello, alertas), mas não o conteúdo.
  3. Zona C: proxy - servidor de destino. Esta é uma conexão TCP separada que o proxy estabelece em seu próprio nome, a partir do seu endereço externo. Na máquina cliente ela simplesmente não existe. Só é possível vê-la no próprio servidor proxy, e isso é infraestrutura do provedor. Tudo o que você descobre sobre a zona C, você descobre indiretamente: pelos códigos de resposta ao CONNECT, pelas latências e por como o proxy fecha o túnel.

Esta é a ideia-chave de todo o artigo. O dump no cliente não mostra o servidor de destino diretamente. Mas ele mostra o comportamento do proxy, e o proxy, como bom retransmissor, traduz o comportamento do servidor de destino para uma forma que pode ser interpretada. Nossa tarefa é aprender a ler essa tradução.

Proxy HTTP e HTTP aberto

O caso mais transparente. Se o site de destino opera por http sem criptografia, o cliente envia ao proxy uma requisição com URI absoluta:

GET http://example.com/api/items HTTP/1.1
Host: example.com
Proxy-Authorization: Basic dXNlcjpwYXNz
User-Agent: my-client/1.0

No dump tudo é visível: método, caminho, cabeçalhos, corpo, resposta do proxy com o corpo do servidor. Preste atenção ao cabeçalho Proxy-Authorization. São suas credenciais em base64, que ficam no dump em texto claro. Vamos guardar esse fato, ele será útil quando falarmos sobre enviar o dump para terceiros.

Proxy HTTP e HTTPS via CONNECT

Aqui começa o mais interessante. Para HTTPS, o cliente primeiro pede ao proxy que estabeleça um túnel TCP até o host e porta:

CONNECT api.example.com:443 HTTP/1.1
Host: api.example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz
Proxy-Connection: Keep-Alive

O proxy responde:

HTTP/1.1 200 Connection established

A partir desse momento o proxy deixa de analisar HTTP. Ele simplesmente copia bytes de um socket para o outro. O cliente começa o handshake TLS diretamente com o servidor de destino, e todo o tráfego HTTP (GET, POST, cabeçalhos, corpos) fica dentro do TLS. É por isso que no dump de um túnel HTTPS só aparece o CONNECT: é a última coisa que o cliente diz ao proxy em texto claro. Depois vêm registros TLS, que o proxy não consegue ler, e você também não no dump.

O que permanece visível dentro do túnel:

  • ClientHello: versões TLS, conjunto de cifras, extensões e, via de regra, o SNI (nome do host em texto claro).
  • ServerHello: versão e cifra escolhidas.
  • Em TLS 1.2 - o certificado do servidor em texto claro. Em TLS 1.3 o certificado já está criptografado.
  • TLS Alert: o tipo de alerta é visível em TLS 1.2; em TLS 1.3 os alertas após o handshake estão criptografados, mas o próprio fato de aparecer um registro do tipo Alert com 2 bytes é reconhecido.
  • Tamanhos e timings dos registros Application Data. Por eles dá para estimar quantos dados chegaram antes da conexão ser interrompida.

SOCKS5

O SOCKS5 funciona de outra forma, mas a ideia é a mesma. O cliente envia a saudação com métodos de autenticação (bytes 05 02 00 02), o proxy escolhe o método, depois vem a autenticação por usuário e senha (subprotocolo com bytes 01, comprimento, nome, comprimento, senha), depois o comando CONNECT com tipo de endereço 03 (nome de domínio) e porta. O proxy responde 05 00 em caso de sucesso ou um código de erro: 01 erro geral, 03 rede inacessível, 04 host inacessível, 05 conexão recusada, 06 TTL expirou. Esses códigos de erro são uma tradução direta do que o proxy viu na zona C. O Wireshark sabe decodificar SOCKS se você indicar a porta via Decode As.

O que nunca é visível

Da máquina cliente você não verá: o IP externo do proxy com o qual ele acessa o servidor de destino (para proxies móveis, esse é o endereço da operadora), a conexão TCP proxy - servidor, as consultas DNS do proxy. Se o suporte do Proxeon diz que vê o erro do lado do servidor de destino, ele está olhando justamente a zona C, inacessível para você. Sua tarefa é trazer o dump da zona A para comparar as duas imagens por tempo.

Mergulho profundo: por que o proxy não consegue mostrar mais e como ler os indícios indiretos

Vale a pena entender por que o quadro é assim e o que disso se segue para o diagnóstico.

O túnel CONNECT como um cano de bytes

Depois da resposta 200, o proxy HTTP, por especificação, é obrigado a transmitir bytes nos dois sentidos sem interpretação até o fechamento por qualquer um dos lados. Ele não sabe o que tem dentro do TLS. Não sabe quais requisições HTTP você está enviando. Ele vê apenas dois eventos: o fluxo de bytes e o fechamento do socket. Quando o servidor de destino fecha a conexão com o proxy (envia FIN ou RST), o proxy é obrigado a fechar a conexão com você. Como exatamente ele fará isso depende da implementação: alguns proxies traduzem FIN como FIN e RST como RST, outros transformam tudo em FIN, outros ainda, ao ler erro no socket remoto, enviam RST ao cliente.

Consequência prática: um RST vindo do endereço IP do proxy não significa que o proxy é o culpado. Significa que o túnel foi fechado do lado do proxy, e a causa pode estar em qualquer lugar atrás dele. Para entender a causa, olhamos o contexto: o que aconteceu antes do RST, quanto tempo passou, se a resposta chegou.

Diferença entre SYN para o proxy e SYN para o servidor de destino

Esta é a fonte mais comum de confusão. Em um dump através de proxy você nunca verá um SYN para a porta 443 do servidor de destino. Todos os SYN vão para o IP e porta do proxy. Se o SYN para o proxy não recebe SYN-ACK e se repete com intervalos de 1, 2, 4, 8 segundos, o problema está entre você e o proxy: rede, firewall, endereço ou porta errados, proxy não está rodando. O servidor de destino não tem nada a ver com isso, você nem tentou alcançá-lo em termos do seu dump.

Já se o TCP com o proxy foi estabelecido, o CONNECT foi enviado e a resposta chega após 20-30 segundos com código 504 Gateway Timeout ou 502 Bad Gateway, isso significa que o proxy não conseguiu estabelecer a zona C. O timing aqui fala por si: seu pacote CONNECT saiu instantaneamente, não houve resposta por muito tempo, então o proxy esperou um timeout na própria tentativa.

TLS 1.3, ECH e o que muda até 2026

O cenário vai se fechando lentamente. O TLS 1.3 domina, e nele o certificado do servidor está criptografado, então verificar qual certificado o servidor devolveu, pelo dump sem chaves, já não é possível. A extensão ECH (Encrypted Client Hello) vai sendo adotada gradualmente por navegadores e grandes CDNs, e lá o SNI também passa a ser criptografado. Para diagnóstico através de proxy isso é menos crítico, porque o nome do host de qualquer forma aparece na linha CONNECT, mas o filtro habitual por SNI vai disparar com menos frequência.

Outra tendência é o HTTP/3 sobre QUIC. Um proxy HTTP clássico com CONNECT tunela apenas TCP. Se no dump você de repente vir tráfego UDP na porta 443 diretamente para o endereço do servidor de destino, ignorando o proxy, é sinal de vazamento: a aplicação tenta usar QUIC diretamente, e não através do proxy. Um cliente configurado corretamente deve ou desabilitar QUIC ao trabalhar através de proxy, ou usar proxy de UDP através de mecanismos separados. Filtro para verificação rápida: udp.port == 443. Se o dump através de proxy mostra esses pacotes, a configuração do cliente precisa ser corrigida.

Onde colocar o ponto de captura

Normalmente a resposta é uma só - na máquina cliente. Mas há nuances. Se o cliente roda em contêiner Docker, o tcpdump no host na interface docker0 ou na interface da bridge mostrará o tráfego antes do NAT, e na interface externa - depois do NAT, com outro endereço de origem. O mais simples é entrar no espaço de rede do contêiner: nsenter -t PID -n tcpdump ... ou iniciar um contêiner com tcpdump via --net=container:nome. Em máquinas virtuais na nuvem, atenção ao offloading: os pacotes no dump podem parecer maiores que o MTU, porque a placa de rede junta segmentos. Isso é normal e não afeta a análise das flags.

tcpdump na prática: filtros, gravação em arquivo, rotação

O tcpdump está em praticamente qualquer máquina Linux e no macOS. No Windows o papel equivalente é desempenhado pelo Wireshark com o driver Npcap ou pelo dumpcap de linha de comando. Vamos ver os comandos que cobrem 95 por cento das tarefas de diagnóstico de proxy.

Captura básica de tráfego para o proxy

Digamos que o endereço do proxy seja 203.0.113.10, porta 8080. Gravamos tudo que circula entre nós e o proxy em um arquivo:

sudo tcpdump -i any -nn -s 0 -w proxy.pcap 'host 203.0.113.10 and port 8080'

Análise das chaves:

  • -i any - escutar todas as interfaces. Conveniente quando você não sabe por qual o tráfego sai. Se sabe, melhor indicar a específica: -i eth0 ou -i wlan0. No macOS -i any não é suportado, indique en0.
  • -nn - não resolver IP em nomes nem portas em nomes de serviços. A resolução desacelera a captura e adiciona consultas DNS extras na rede.
  • -s 0 - capturar o pacote inteiro. Em versões modernas este já é o valor padrão, mas indicar explicitamente não faz mal.
  • -w proxy.pcap - gravar em arquivo no formato pcap, em vez de exibir na tela. Só assim o dump pode ser aberto depois no Wireshark.
  • Filtro entre aspas simples - é um filtro BPF de captura. Ele corta tudo o que é desnecessário ainda no kernel, então o arquivo conterá apenas o necessário.

Filtros por host e porta

Vários proxies ou pool de portas:

sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and portrange 10000-10100'

Proxy por nome de domínio (o tcpdump vai resolver uma vez na inicialização, o que nem sempre serve para endereços rotativos):

sudo tcpdump -i eth0 -nn -w proxy.pcap 'host proxy.example.net and port 8080'

Captura apenas de pacotes de serviço sem dados, para olhar handshakes e quedas com grande volume de tráfego:

sudo tcpdump -i eth0 -nn -w flags.pcap 'host 203.0.113.10 and tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'

Apenas RST nos dois sentidos:

sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

Captura com exclusão da própria sessão SSH, para não poluir o dump quando você o captura em um servidor remoto:

sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and not port 22'

Como não acumular gigabytes: rotação por tamanho e tempo

Se o erro é raro e se reproduz uma vez por hora, o dump precisa rodar por muito tempo. Sem rotação, o disco acaba. Rotação por tamanho, 100 MB por arquivo, anel de 10 arquivos:

sudo tcpdump -i eth0 -nn -w proxy-%Y%m%d-%H%M%S.pcap -C 100 -W 10 'host 203.0.113.10 and port 8080'

Aqui -C define o tamanho em milhões de bytes, -W limita a quantidade de arquivos: o décimo primeiro arquivo sobrescreverá o primeiro. Rotação por tempo, novo arquivo a cada 10 minutos, mantendo os últimos 24 arquivos (4 horas):

sudo tcpdump -i eth0 -nn -w proxy-%Y%m%d-%H%M%S.pcap -G 600 -W 24 'host 203.0.113.10 and port 8080'

A chave -G indica o intervalo em segundos, e o modelo de tempo no nome do arquivo é obrigatório, senão o tcpdump vai sobrescrever um único arquivo. A chave útil -Z usuário descarta privilégios depois de abrir a interface, e -U força a gravação dos pacotes no arquivo imediatamente, sem buffer, o que é importante se você for ler o arquivo em paralelo ou teme perder o final em caso de encerramento anormal.

Recorte da carga útil

Mais uma forma de reduzir o volume - capturar apenas cabeçalhos. Para diagnóstico de quedas o conteúdo dos registros TLS não é necessário, bastam os primeiros 128 bytes de cada pacote:

sudo tcpdump -i eth0 -nn -s 128 -w headers.pcap 'host 203.0.113.10'

Cuidado: com -s 128 a linha CONNECT e os cabeçalhos podem ser cortados, e o Wireshark marcará os pacotes como truncated. Para análise completa do handshake TLS isso é pouco, o ClientHello costuma ocupar 300-600 bytes ou mais. O compromisso é -s 600.

Leitura do dump direto no console

Nem sempre o Wireshark está à mão, e é preciso dar uma olhada rápida. Leitura de arquivo com exibição das flags TCP e números de sequência absolutos:

tcpdump -nn -r proxy.pcap -S -tttt 'tcp[tcpflags] & (tcp-rst|tcp-fin) != 0'

A chave -tttt imprime data e hora completas, -S mostra os sequence numbers absolutos, o que ajuda a correlacionar com os logs. Exibição do conteúdo dos pacotes em ASCII, para ver a linha CONNECT e a resposta do proxy:

tcpdump -nn -r proxy.pcap -A 'port 8080' | grep -E 'CONNECT|HTTP/1'

tshark como Wireshark de console

Se o Wireshark está instalado no servidor, o tshark dá acesso aos mesmos dissectors pelo console. Lista de todos os CONNECT com códigos de resposta:

tshark -r proxy.pcap -Y 'http.request.method == "CONNECT" or http.response.code' -T fields -e frame.time -e tcp.stream -e http.request.uri -e http.response.code

Lista de fluxos que terminaram em RST, indicando quem o enviou:

tshark -r proxy.pcap -Y 'tcp.flags.reset == 1' -T fields -e frame.time_relative -e tcp.stream -e ip.src -e ip.dst

Wireshark: filtros de exibição, Follow TCP Stream, leitura de TLS sem descriptografia

Você abriu o pcap no Wireshark e viu milhares de linhas. Por onde começar? Pelos filtros de exibição. Diferente dos filtros BPF de captura, eles funcionam por cima do arquivo já gravado e entendem a estrutura dos protocolos.

Filtros básicos para trabalhar através de proxy

Tudo que está relacionado ao proxy:

ip.addr == 203.0.113.10 && tcp.port == 8080

Todas as requisições CONNECT:

http.request.method == "CONNECT"

CONNECT para um host específico:

http.request.method == "CONNECT" && http.host contains "api.example.com"

Respostas do proxy diferentes de 200 (erros de autorização 407, indisponibilidade 502, timeouts 504):

http.response.code >= 400 && tcp.port == 8080

Apenas respostas 407, sinal de credenciais erradas ou limite esgotado:

http.response.code == 407

Filtros TLS dentro do túnel

O Wireshark reconhece que, após um CONNECT com resposta 200, começa TLS, e substitui automaticamente o dissector TLS. Se não reconhecer (acontece com pacotes cortados), clique com o botão direito no pacote, escolha Decode As e indique TLS para essa porta.

Todos os ClientHello:

tls.handshake.type == 1

ClientHello com SNI específico:

tls.handshake.extensions_server_name contains "example.com"

ServerHello (se ele não aparece após o ClientHello - o handshake não começou do lado do servidor):

tls.handshake.type == 2

Alertas TLS:

tls.alert_message

Encontrar o fluxo onde há ClientHello, mas não há ServerHello, é mais difícil com um único filtro; é mais prático via Statistics > Conversations, ordenar pela quantidade de pacotes e olhar fluxos com 5-7 pacotes.

Filtros por problemas de TCP

Todos os pacotes com RST:

tcp.flags.reset == 1

RST enviados pelo proxy na nossa direção:

tcp.flags.reset == 1 && ip.src == 203.0.113.10

RST enviados por nós:

tcp.flags.reset == 1 && ip.dst == 203.0.113.10

Retransmissões:

tcp.analysis.retransmission

Janela zero e suas consequências:

tcp.analysis.zero_window || tcp.analysis.window_full

Todas as anomalias que o próprio Wireshark notou (retransmissões, ACKs duplicados, segmentos perdidos, janela zero):

tcp.analysis.flags && !tcp.analysis.window_update

SYN sem resposta, ou seja, SYNs repetidos:

tcp.flags.syn == 1 && tcp.flags.ack == 0 && tcp.analysis.retransmission

Grandes pausas dentro do fluxo, mais de 5 segundos entre pacotes:

tcp.time_delta > 5

Para esse filtro é preciso ativar Edit > Preferences > Protocols > TCP > Calculate conversation timestamps.

Follow TCP Stream

Você encontrou um pacote suspeito, por exemplo um RST. Clica com o botão direito > Follow > TCP Stream. O Wireshark mostrará todo o diálogo dessa conexão como texto: nosso tráfego em uma cor, o tráfego do proxy em outra. Para HTTPS via CONNECT você verá a linha CONNECT, a resposta 200 Connection established, e depois bytes ilegíveis de TLS. Isso é normal. O que deve ser olhado são os volumes: quantos bytes saíram de nós (nosso ClientHello e requisições), quantos chegaram (ServerHello e resposta) e onde tudo terminou.

No rodapé da janela Follow há contadores: tantos bytes cliente, tantos bytes servidor. Se do servidor vieram 0 bytes após a resposta 200, o servidor de destino não respondeu ao ClientHello de forma alguma. Se vieram cerca de 100-4000 bytes e cortou, o handshake começou, mas não foi concluído. Se vieram dezenas de kilobytes e depois a queda, o problema está no meio da transferência de dados.

Hábito útil: filtrar pelo número do fluxo. Depois do Follow, a linha de filtro automaticamente mostrará tcp.stream eq 42. Feche a janela Follow, e na lista principal restará apenas esse fluxo com as flags e timings. Esta é a visão mais prática para analisar a queda.

Leitura do handshake TLS sem descriptografia

Expanda o ClientHello na árvore do pacote. O que observar:

  • Version e supported_versions - o cliente está oferecendo TLS 1.3. Se o servidor exige 1.3, mas o cliente só oferece 1.2, o servidor fechará a conexão com um alerta protocol_version ou simplesmente FIN.
  • server_name - o SNI coincide com o host do CONNECT. Divergência ocorre com configuração errada do cliente e leva a erros de certificado.
  • Cipher Suites - lista de cifras. Uma lista curta ou desatualizada é causa de alerta handshake_failure.
  • ALPN - o cliente está oferecendo h2. Se o servidor escolheu h2, mas o cliente internamente esperava HTTP/1.1, a aplicação pode falhar com erros estranhos.

No ServerHello observe a versão e a cifra escolhidas. Em TLS 1.2 depois vem o Certificate em texto claro, dá para verificar nome e validade. Em TLS 1.3 após o ServerHello quase tudo está criptografado, e o próximo item legível é o tipo de registro. Application Data significa que o handshake terminou com sucesso e os dados começaram a fluir. Um Alert de 2 bytes logo após o ServerHello ou em seu lugar significa que algo está errado: em TLS 1.3 você não verá o código do alerta sem as chaves, mas o próprio fato já é informativo.

Em Statistics > Conversations > TCP é prático avaliar o panorama geral: quantos fluxos, quantos bytes em cada direção, duração. Fluxos com duração de 0,05 segundos e 6 pacotes são, muito provavelmente, conexões resetadas logo após o handshake.

Diagnóstico pelo dump: RST, FIN, retransmission, zero window

Agora o principal. Como, pelo dump da zona A, entender onde exatamente rompeu? Vamos ver cada sinal e sua interpretação no contexto de proxy.

Matriz de direções

A primeira pergunta é sempre a mesma: quem enviou o pacote de fechamento? No dump isso é o campo ip.src. Há apenas duas opções - nosso endereço ou o endereço do proxy.

  • RST ou FIN de nós - a conexão foi fechada pela nossa aplicação, pelo nosso SO ou por algo no nosso host. O proxy e o servidor de destino não participaram. Causas típicas: timeout no cliente HTTP, encerramento anormal do processo, esgotamento de descritores de arquivo, atuação de firewall ou antivírus local.
  • RST ou FIN do proxy - o túnel foi fechado do lado do proxy. A causa está ou no próprio proxy (limites, política, timeout de ociosidade), ou foi traduzida do servidor de destino, ou da rede entre proxy e servidor. Distinguimos pelo contexto e pelos timings.
  • Nem RST nem FIN, apenas retransmissões - os pacotes estão se perdendo no caminho entre nós e o proxy. Nenhum dos lados fechou a conexão, ela simplesmente morreu por perdas.

RST após CONNECT sem resposta 200

Quadro: TCP estabelecido, enviamos o CONNECT, o proxy responde RST sem resposta HTTP. Esse comportamento geralmente significa que o proxy recusou no nível de política ou não conseguiu interpretar a requisição. Causas: porta de destino não permitida, limite de conexões simultâneas, formato de requisição incorreto. Aqui o problema está na zona A ou no próprio proxy. O servidor de destino não tem nada a ver.

Resposta 502, 503 ou 504 ao CONNECT

Quadro: o CONNECT saiu, após N segundos chegou uma resposta HTTP com código 5xx. Olhamos o N. Se a resposta chegou em tempo da ordem do RTT até o proxy, o proxy recusou imediatamente - talvez o nome DNS não resolva do lado dele ou o endereço esteja indisponível imediatamente (ICMP unreachable). Se N está em torno de 10-30 segundos, o proxy esperou timeout ao conectar ao servidor de destino: o servidor não responde ao SYN. Em ambos os casos é zona C, e seu dump prova que você fez tudo certo, e que o proxy honestamente comunicou a impossibilidade.

FIN ou RST logo após o ClientHello

Quadro: CONNECT - 200 - nosso ClientHello - após um RTT até o proxy mais algo chega FIN ou RST do proxy. Aqui há dois candidatos: o servidor de destino rejeitou a conexão na etapa TLS (não gostou do SNI, da versão, da ausência de certificado do cliente) ou o sistema de defesa do servidor resetou a conexão por características do ClientHello. O indício-chave é o tempo. Se entre nosso ClientHello e a queda passou um tempo visivelmente maior do que o RTT até o proxy, então o proxy conseguiu enviar o ClientHello adiante e receber a reação. Não foi o proxy que decidiu fechar, foi a reação traduzida do servidor de destino.

Compare com o RTT até o proxy, que você mede pelo handshake TCP: o tempo entre SYN e SYN-ACK. Se o RTT até o proxy é 40 ms, e a queda após o ClientHello veio após 200 ms, a diferença de 160 ms é aproximadamente o RTT proxy - servidor de ida e volta. O quadro é totalmente consistente com a recusa do servidor.

Queda após o ServerHello ou após parte dos dados

O handshake começou, o servidor respondeu, os dados começaram a fluir, e no meio vem FIN ou RST. Se for FIN e do proxy, e antes dele o último registro Application Data tem tamanho lógico, talvez o servidor simplesmente tenha fechado a conexão após a resposta (Connection: close dentro do TLS), e o cliente interpretou isso incorretamente. Se for RST no meio do fluxo de dados sem queda de ritmo anterior, é ou fechamento forçado no servidor, ou o proxy interrompeu o túnel por limite de tráfego ou tempo de vida da sessão. Para proxies móveis com rotação de IP por tempo, a segunda hipótese é muito provável: a queda ocorre exatamente no momento da troca de IP. Verifique se o horário da queda coincide com o intervalo de rotação configurado no seu proxy.

Retransmissões e sua direção

O Wireshark marca um pacote como retransmission quando vê retransmissão dos mesmos sequence numbers. Olhamos quem retransmite:

  • Retransmitimos nós - nossos pacotes não são confirmados pelo proxy. Ou se perdem no caminho até o proxy, ou as confirmações se perdem no caminho de volta. Em ambos os casos o problema está na rede da zona A.
  • Retransmite o proxy - os pacotes dele não são confirmados por nós. Não os recebemos ou nossos ACKs não chegam. Também zona A, mas com viés para a direção de entrada.
  • Retransmissões de SYN - caso à parte: o proxy está inacessível no nível TCP, a conexão não se estabelece de forma alguma.

Importante: perdas na zona C você nunca verá como retransmissões. O proxy mesmo resolve com o servidor de destino. O único rastro de perdas na zona C são pausas: o proxy nos transmite dados de forma irregular, com lacunas, embora não haja retransmissões no dump. O filtro tcp.time_delta > 1 para pacotes do proxy mostrará essas pausas.

Zero Window

Uma janela TCP de tamanho zero significa que o receptor não está dando conta de consumir os dados do buffer do socket. Se a janela zero é anunciada por nós, nossa aplicação não lê a resposta rápido o suficiente: ocupada, bloqueada, processamento lento. O proxy nesse caso espera, depois envia Zero Window Probe e, se a aplicação continuar sem ler, após algumas dezenas de segundos pode fechar a conexão. O cliente verá a queda e culpará o proxy, embora a causa esteja no cliente.

Se a janela zero é anunciada pelo proxy, significa que ele não está conseguindo transmitir nossos dados adiante - o servidor de destino aceita lentamente. Este é um indício indireto de problemas na zona C, ocorre ao baixar arquivos grandes através de proxy para um servidor lento.

ACKs duplicados e SACK

ACK duplicado do proxy é sinal de que ele recebeu um pacote fora de ordem, algo nosso se perdeu. Muitos ACKs duplicados e retransmissões subsequentes - quadro clássico de perdas em rede móvel ou Wi-Fi do nosso lado. ACKs duplicados nossos - pacotes do proxy se perderam. A presença da opção SACK no handshake permite ao TCP se recuperar com mais eficiência, mas o fato das perdas continua evidente.

Heurística de TTL

Técnica avançada. Olhe o campo IP TTL em pacotes normais do proxy e no pacote RST. Se o TTL no RST difere em várias unidades, o RST não foi gerado pelo próprio proxy, mas por um nó intermediário no caminho: firewall, balanceador ou sistema de filtragem da operadora. Não é prova absoluta, mas é um forte indício de que vale a pena verificar o caminho entre você e o proxy, em vez de culpar o proxy ou o servidor de destino.

Tabela resumo de interpretações

  • SYN se repete, SYN-ACK não vem: proxy inacessível ou rede até ele. Zona A.
  • SYN - RST: porta do proxy fechada ou filtrada. Zona A.
  • CONNECT - 407: autorização errada ou limite da conta. Proxy.
  • CONNECT - 502/504 rápido: o proxy não conseguiu iniciar a conexão com o servidor. Zona C, provavelmente DNS ou rota.
  • CONNECT - 504 após 20-30 segundos: o servidor não responde ao SYN do proxy. Zona C.
  • 200 - ClientHello - RST/FIN após tempo maior que o RTT: o servidor rejeitou o TLS. Zona C.
  • 200 - ClientHello - silêncio - queda por timeout do cliente: o servidor aceita TCP, mas não responde ao TLS. Zona C ou filtro bloqueante atrás do proxy.
  • Dados fluindo - RST do proxy em momento fixo: limite ou rotação do proxy. Proxy.
  • Retransmissões e ACKs duplicados sem RST: perdas na rede entre nós e o proxy. Zona A.
  • Zero Window de nós: nossa aplicação não lê. Cliente.
  • RST de nós após longo silêncio: nosso timeout. Cliente.

Descriptografia do próprio tráfego via SSLKEYLOGFILE

Às vezes as flags não bastam e é preciso ver o que exatamente o servidor devolveu dentro do TLS: código de resposta, cabeçalhos, corpo do erro. Para isso não é preciso mitmproxy nem certificado falso. Basta que seu próprio cliente grave as chaves de sessão em um arquivo, e o Wireshark as use para descriptografar. Isso funciona apenas para o seu cliente e suas conexões: as chaves estão somente do lado que participou do handshake. Descriptografar tráfego alheio ou o tráfego que o proxy conduz com o servidor na zona C, por esse método, é impossível, e isso é correto.

Como habilitar em diferentes clientes

Navegadores baseados em Chromium e Firefox leem a variável de ambiente SSLKEYLOGFILE e gravam nela as chaves no formato NSS Key Log:

export SSLKEYLOGFILE=/home/user/tls-keys.log
google-chrome --proxy-server=http://203.0.113.10:8080

O curl, compilado com OpenSSL, também lê essa variável:

export SSLKEYLOGFILE=/home/user/tls-keys.log
curl -v -x http://user:pass@203.0.113.10:8080 https://api.example.com/health

Node.js com a chave de linha de comando:

node --tls-keylog=/home/user/tls-keys.log app.js

Python não lê a variável automaticamente, mas a partir da versão 3.8 o SSLContext tem o atributo keylog_filename. Para requests via adaptador:

import os, ssl, requests
from requests.adapters import HTTPAdapter

class KeyLogAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
ctx = ssl.create_default_context()
ctx.keylog_filename = os.environ.get("SSLKEYLOGFILE")
kwargs["ssl_context"] = ctx
return super().init_poolmanager(*args, **kwargs)

s = requests.Session()
s.mount("https://", KeyLogAdapter())
s.proxies = {"https": "http://user:pass@203.0.113.10:8080"}
r = s.get("https://api.example.com/health", timeout=30)
print(r.status_code)

Go via campo KeyLogWriter em tls.Config:

f, _ := os.OpenFile(os.Getenv("SSLKEYLOGFILE"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
tlsCfg := &tls.Config{KeyLogWriter: f}
tr := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: tlsCfg,
}

Conexão das chaves no Wireshark

Duas formas. Primeira: Edit > Preferences > Protocols > TLS > (Pre)-Master-Secret log filename, indicar o caminho do arquivo de chaves. O Wireshark descriptografará todos os fluxos para os quais encontrar correspondência pelo Client Random. A segunda forma é mais robusta para enviar o arquivo a colegas: embutir as chaves diretamente no pcapng:

editcap --inject-secrets tls,/home/user/tls-keys.log proxy.pcap proxy-with-keys.pcapng

Depois disso, no Wireshark, dentro do túnel CONNECT aparecerão requisições e respostas HTTP/1.1 ou HTTP/2 descriptografadas. Filtro para HTTP/2 descriptografado:

http2.header.name == ":status"

Para HTTP/1.1 descriptografado dentro do túnel, funcionam os filtros usuais http.response.code e http.request.uri.

O que isso dá ao diagnóstico através de proxy

A descriptografia remove a última incerteza. Você vê que o servidor respondeu 429 com cabeçalho Retry-After e depois fechou a conexão, ou que devolveu 200, mas o corpo cortou em 40 por cento, ou que a requisição saiu e não houve resposta alguma. Com um quadro desses, o contato com o suporte do proxy passa a ser objetivo: ou o problema está claramente no servidor, ou claramente no túnel.

Regras de segurança

  • O arquivo de chaves permite descriptografar completamente as sessões gravadas, incluindo cookies e tokens. Guarde-o como uma senha e apague-o após a análise.
  • Não envie o arquivo de chaves junto com o dump para o suporte do proxy nem para mais ninguém. O suporte, para analisar quedas, não precisa das chaves, basta o quadro de flags e timings.
  • Se ainda assim for necessário mostrar o conteúdo descriptografado, faça isso com uma conta de teste no serviço de destino e credenciais de teste do proxy.
  • Não deixe o SSLKEYLOGFILE ativado em produção. Uma variável de ambiente que caia por acidente em uma configuração de serviço gravará chaves em disco por anos.

Quadros típicos: timeout de conexão, queda no meio da resposta, perdas em rede móvel

Vamos reunir os indícios analisados em cenários reconhecíveis. Cada um é descrito como aparece na lista de pacotes do Wireshark após o filtro tcp.stream eq N.

Quadro 1: timeout de conexão ao proxy

0.000 client -> proxy [SYN]
1.002 client -> proxy [SYN] (TCP Retransmission)
3.006 client -> proxy [SYN] (TCP Retransmission)
7.010 client -> proxy [SYN] (TCP Retransmission)
15.02 client -> proxy [SYN] (TCP Retransmission)

Nem um pacote do proxy. Os intervalos exponenciais de 1, 2, 4, 8 segundos são o retransmission backoff padrão do kernel Linux. Diagnóstico: o proxy está inacessível do seu ponto. Verifique endereço, porta, firewall, roteamento, e também se o endereço do proxy não mudou. Se for um proxy móvel Proxeon com porta dedicada, certifique-se de que a porta do seu painel pessoal coincide com a do arquivo de configuração do cliente.

Quadro 2: timeout do proxy ao conectar ao servidor de destino

0.000 client -> proxy [SYN]
0.041 proxy -> client [SYN, ACK]
0.041 client -> proxy [ACK]
0.042 client -> proxy CONNECT api.example.com:443 HTTP/1.1
0.083 proxy -> client [ACK]
30.10 proxy -> client HTTP/1.1 504 Gateway Timeout
30.10 proxy -> client [FIN, ACK]

RTT até o proxy de 41 ms, o proxy confirmou o recebimento do CONNECT, e depois ficou 30 segundos em silêncio e devolveu 504. Diagnóstico: o servidor de destino não responde ao proxy na tentativa de conexão. Causas possíveis - servidor fora do ar, porta fechada para o pool de endereços do proxy, problemas de rede na rota proxy - servidor. Seu cliente e sua rede não têm nada a ver.

Quadro 3: o servidor rejeita o TLS

0.000 client -> proxy CONNECT api.example.com:443 HTTP/1.1
0.045 proxy -> client HTTP/1.1 200 Connection established
0.046 client -> proxy TLSv1.3 Client Hello (SNI=api.example.com)
0.210 proxy -> client [FIN, ACK]
0.210 client -> proxy [FIN, ACK]

Queda 164 ms após o ClientHello, com RTT até o proxy de 45 ms. A diferença de cerca de 120 ms é o tempo em que o proxy repassou o ClientHello ao servidor e recebeu o fechamento. Diagnóstico: o servidor aceitou o TCP, mas fechou a conexão na etapa TLS. Verifique os parâmetros do handshake: versões, cifras, SNI, ALPN. Se o ServerHello está ausente e não há alerta, o servidor fechou a conexão em silêncio, o que sistemas de defesa costumam fazer diante de um ClientHello atípico.

Quadro 4: queda no meio da resposta

0.000 client -> proxy CONNECT cdn.example.com:443 HTTP/1.1
0.044 proxy -> client HTTP/1.1 200 Connection established
0.045 client -> proxy Client Hello
0.190 proxy -> client Server Hello, Change Cipher Spec, Application Data
0.195 client -> proxy Application Data (request)
0.350 proxy -> client Application Data (1460 bytes)
... mais 340 pacotes de dados
4.812 proxy -> client Application Data (1460 bytes)
4.813 proxy -> client [RST, ACK]

O handshake passou, cerca de 500 KB de dados chegaram, e um RST sem desaceleração, sem retransmissões, sem zero window. Olhamos o tempo absoluto. Se ele coincide com o momento de rotação de IP no proxy móvel ou com o esgotamento do limite de duração da sessão, a causa está no proxy, e a solução é alinhar o intervalo de rotação com a duração das suas requisições ou usar o modo sem rotação durante downloads longos. Se não há coincidência, é provável que a queda tenha sido do lado do servidor ou da CDN. Aqui ajuda a descriptografia via SSLKEYLOGFILE: se dentro for visível o cabeçalho Content-Length e o corpo não chegou por completo, o servidor interrompeu a transmissão.

Quadro 5: perdas em rede móvel do lado do cliente

0.000 client -> proxy Application Data (1460 bytes)
0.001 client -> proxy Application Data (1460 bytes)
0.002 client -> proxy Application Data (1460 bytes)
0.180 proxy -> client [ACK] (Dup ACK 1)
0.181 proxy -> client [ACK] (Dup ACK 2)
0.182 proxy -> client [ACK] (Dup ACK 3)
0.183 client -> proxy Application Data (TCP Fast Retransmission)
0.420 client -> proxy Application Data (TCP Retransmission)
0.900 client -> proxy Application Data (TCP Retransmission)

ACKs duplicados do proxy, seguidos de retransmissões nossas com intervalos crescentes. Nem RST, nem FIN. Diagnóstico: perdas no caminho cliente - proxy. Se o cliente está conectado via rede celular ou Wi-Fi, isso é esperado nos momentos de troca entre estações base. A solução é aumentar os timeouts do cliente, ativar TCP keepalive, não manter conexões longas ociosas, porque o NAT das operadoras remove registros de conexões idle, geralmente após 30-300 segundos, e o próximo pacote se perde no vazio. Filtro para avaliar a extensão das perdas:

tcp.analysis.retransmission && ip.dst == 203.0.113.10

Conte a porcentagem desses pacotes em relação ao total via Statistics > Capture File Properties. Um ou dois por cento é tolerável para rede móvel, mais de cinco - procure o problema nas condições de rádio ou no equipamento.

Quadro 6: nosso próprio timeout

0.000 client -> proxy CONNECT api.example.com:443 HTTP/1.1
0.044 proxy -> client HTTP/1.1 200 Connection established
0.045 client -> proxy Client Hello
0.200 proxy -> client Server Hello ... Application Data
0.205 client -> proxy Application Data (request)
0.250 proxy -> client [ACK]
10.21 client -> proxy [FIN, ACK]
10.25 proxy -> client [FIN, ACK]

A requisição saiu, o proxy confirmou, a resposta não veio, e exatamente após 10 segundos enviamos o FIN. Este é o nosso read timeout. O erro no log do cliente parecerá uma queda, mas pelo dump fica claro: fomos nós que fechamos a conexão, porque o servidor pensou por mais tempo do que estamos dispostos a esperar. A solução é ou aumentar o timeout, ou investigar por que o servidor está lento, mas o proxy aqui simplesmente esperou honestamente junto conosco.

Erros típicos ao capturar e ler o dump

Vamos listar aqueles em que até engenheiros experientes tropeçam com regularidade.

  • Capturar dump sem filtro de captura em máquina carregada. Em um minuto acumula um gigabyte, e achar os 200 pacotes necessários nele é desconfortável. Sempre filtre pelo host do proxy.
  • Filtrar pelo endereço do servidor de destino. Através de proxy esses pacotes não existem na sua máquina. O filtro retornará vazio, e a pessoa decide que o tráfego não está passando. Está passando, mas para o proxy.
  • Confundir filtro de captura com filtro de exibição. A sintaxe BPF no tcpdump (host, port, tcp[tcpflags]) e a sintaxe do Wireshark (ip.addr, tcp.port, tcp.flags.reset) são diferentes. Um filtro Wireshark no tcpdump dará erro de sintaxe, e vice-versa.
  • Ver o RST do proxy e culpar imediatamente o proxy. RST do endereço do proxy é o fechamento do túnel, e não uma admissão de culpa. Olhe os timings e o que houve antes do RST.
  • Ignorar o RTT. Sem a latência medida até o proxy, é impossível distinguir a recusa instantânea do proxy da recusa traduzida do servidor.
  • Capturar dump com -s 64 e depois tentar ler o CONNECT. Os cabeçalhos ficarão cortados. Para diagnóstico de proxy é preciso captura completa ou pelo menos -s 600.
  • Não sincronizar o relógio. Se o relógio da máquina com o dump atrasa um minuto, correlacionar o dump com os logs do suporte não será possível. Ative o NTP e indique UTC no contato.
  • Enviar dump com credenciais do proxy. O cabeçalho Proxy-Authorization em requisição HTTP aberta ao proxy contém login e senha. Ou capture o dump com dados de teste, ou troque a senha após o envio.
  • Enviar o arquivo de chaves junto com o dump. Isso expõe todo o conteúdo das sessões. O suporte não precisa das chaves para analisar quedas.
  • Manter um único dump longo sem rotação. O disco encherá no momento mais inoportuno, e o tcpdump cairá junto com os pacotes necessários.
  • Não verificar vazamento de QUIC. UDP na 443 diretamente é sinal de que parte do tráfego passa por fora do proxy, e o diagnóstico pelo túnel TCP não mostrará nada.
  • Esquecer do segmentation offload. Pacotes de 60 KB no dump em máquina virtual não significam MTU errado. É o kernel entregando ao tcpdump segmentos ainda não fatiados.

Ferramentas e recursos

Conjunto mínimo para trabalhar com a metodologia descrita.

Captura

  • tcpdump - padrão no Linux e no macOS. Instalado em quase todo lugar, exige root ou a capability CAP_NET_RAW.
  • dumpcap - capturador de console que faz parte do Wireshark, suporta as mesmas chaves de rotação (-b filesize, -b files) e o formato pcapng.
  • Wireshark com Npcap - para Windows. Dá para capturar direto da GUI com filtro de captura na mesma sintaxe BPF.
  • tshark - análise de console com os dissectors do Wireshark, prático em servidores sem interface gráfica e para automação.

Análise

  • Wireshark - ferramenta principal. Follow TCP Stream, Expert Info, Statistics > Conversations, IO Graph para visualizar pausas e picos de retransmissões.
  • editcap - fatiamento e filtragem de pcap, injeção de chaves TLS no pcapng.
  • mergecap - junção de arquivos de rotação em um só para análise.
  • capinfos - resumo rápido do arquivo: duração, quantidade de pacotes, tamanho.

Clientes com suporte a depuração

  • curl com as chaves -v e --trace-time duplica o quadro do dump no nível da aplicação e suporta SSLKEYLOGFILE.
  • openssl s_client -proxy host:port permite executar manualmente o CONNECT e o handshake TLS através do proxy e ver a resposta do servidor sem cliente HTTP.

One-liners úteis

Colar arquivos de rotação:

mergecap -w all.pcapng proxy-*.pcap

Recortar do dump apenas um fluxo para enviar ao suporte:

tshark -r all.pcapng -Y 'tcp.stream eq 42' -w stream42.pcapng

Estatística rápida de fluxos com RST:

tshark -r all.pcapng -q -z conv,tcp | head -40

Verificação manual do CONNECT via openssl:

openssl s_client -proxy 203.0.113.10:8080 -connect api.example.com:443 -servername api.example.com -brief

Casos e resultados

Caso 1: dois por cento de quedas, o culpado era o cliente

Um serviço de coleta de preços operava através de um pool de proxies móveis, cerca de 40 mil requisições por dia. Aproximadamente 2,3 por cento das requisições caíam com RemoteDisconnected. A equipe estava certa de que a culpa era dos proxies. Capturaram dump com rotação de 100 MB por dia, filtraram tcp.flags.reset == 1 e olharam o ip.src. Em 91 por cento dos casos o RST era enviado pelo próprio cliente. A análise dos fluxos mostrou: antes do RST havia silêncio exatamente de 5 segundos após o envio da requisição, e depois RST do cliente. No código havia um read timeout de 5 segundos, e o servidor de destino, nos horários de pico, respondia em 6-8 segundos. Aumentar o timeout para 15 segundos reduziu os erros a 0,3 por cento. Os 0,3 por cento restantes eram FIN reais do proxy após 160-200 ms do ClientHello: o servidor recusava periodicamente conexões de parte do pool de endereços. Esses dados foram passados ao suporte, que confirmou o quadro pelos seus logs da zona C.

Caso 2: quedas de downloads grandes exatamente a cada 10 minutos

O cliente baixava arquivos de 300-800 MB através de proxy móvel e reclamava de quedas no meio. O dump mostrou RST do proxy em momentos distantes entre si exatamente 600 segundos, com precisão de segundo, independentemente do início do download. A causa estava na rotação de IP configurada por agendamento a cada 10 minutos: ao trocar de endereço o proxy fecha os túneis ativos. A solução foi mudar a porta para rotação sob demanda e disparar a troca de endereço entre downloads. As quedas sumiram completamente, foram necessárias duas horas de diagnóstico em vez de semanas de troca de mensagens.

Caso 3: o servidor está fora do ar, mas parece que a culpa é do proxy

Pela manhã, todas as requisições a uma API começaram a retornar erro de conexão. Logs do cliente: Connection aborted. A primeira reação - o proxy caiu. Um dump de um minuto mostrou: o TCP com o proxy se estabelece em 38 ms, o CONNECT sai, e após 30 segundos chega 504 e FIN. Ao mesmo tempo, uma requisição a outro host pelo mesmo proxy passava em 300 ms. Diagnóstico: o servidor de destino não aceita conexões do proxy. Após 40 minutos, a página de status do serviço de destino confirmou o incidente do lado deles. O dump economizou a manhã e evitou um ticket falso no suporte do proxy.

Caso 4: perdas no Wi-Fi pareciam problema do proxy

Um desenvolvedor testava a integração do notebook através de um proxy móvel Proxeon e recebia timeouts esporádicos. O dump mostrou retransmissões do cliente com frequência de 6 por cento e ACKs duplicados do proxy, sem nenhum RST do proxy. Conectar o notebook por cabo reduziu as retransmissões a zero, e os timeouts sumiram. O problema estava no ponto de acesso sobrecarregado do escritório.

Checklist de captura de dump para contato com o suporte

Se você pretende enviar o dump ao suporte de um serviço de proxy, aqui está a ordem de ações que economizará tempo de ambos os lados.

Preparação

  1. Sincronize o relógio da máquina via NTP. Verifique com o comando timedatectl ou date -u.
  2. Se possível, crie credenciais de teste separadas do proxy para o período de diagnóstico ou planeje a troca de senha após o envio do dump.
  3. Anote a versão do cliente, a biblioteca HTTP, o SO, a forma de conexão à internet (cabo, Wi-Fi, rede celular).
  4. Registre endereço e porta do proxy, host de destino, comportamento esperado e comportamento real.

Captura

  1. Inicie o tcpdump com filtro por host e porta do proxy, com rotação, tamanho completo de pacote:
sudo tcpdump -i eth0 -nn -s 0 -U -w proxy-%Y%m%d-%H%M%S.pcap -G 300 -W 48 'host 203.0.113.10 and port 8080'
  1. Reproduza o problema. Se for raro, deixe a captura pelo tempo necessário; 48 arquivos de 5 minutos cobrem 4 horas.
  2. Simultaneamente à captura, faça uma requisição de controle com curl -v e --trace-time, salve a saída. Ela dará a correspondência precisa de horário com os pacotes.
  3. Registre o horário exato (UTC) de cada manifestação do erro nos logs do cliente.
  4. Pare o tcpdump com Ctrl+C. Confirme que os arquivos não estão vazios: capinfos proxy-*.pcap.

Processamento

  1. Cole os arquivos: mergecap -w all.pcapng proxy-*.pcap.
  2. Encontre os fluxos problemáticos com o filtro tcp.flags.reset == 1 ou pelo horário dos logs.
  3. Recorte apenas os fluxos necessários: tshark -r all.pcapng -Y 'tcp.stream in {12 47 93}' -w report.pcapng. O suporte não precisa de horas de tráfego normal.
  4. Verifique se no dump recortado não há nada de mais: conexões alheias, tráfego de outros serviços.
  5. Não anexe o arquivo de chaves TLS.

Texto do contato

  • Horário de manifestação em UTC com precisão de segundo.
  • Endereço e porta do proxy, host de destino.
  • Interpretação resumida: o que você vê no dump (por exemplo, 200 no CONNECT, depois FIN do proxy 170 ms após o ClientHello, RTT até o proxy de 45 ms).
  • Números dos frames ou fluxos no arquivo anexado.
  • Saída do curl -v com marcas de tempo.
  • O que você já verificou e excluiu: outro host pelo mesmo proxy, o mesmo host por outra conexão.

Um contato assim o suporte processa em uma única rodada. Basta a ele correlacionar suas marcas de tempo com os logs da zona C e confirmar ou refutar a hipótese.

FAQ

Por que no dump não há o endereço IP do servidor de destino, se eu me dirijo a ele?

Porque você não se dirige a ele diretamente, mas através de proxy. Seu cliente estabelece uma conexão TCP com o endereço do proxy e pede que ele se conecte ao host de destino. A conexão proxy - servidor existe apenas do lado do proxy. No dump o nome do host de destino é visível na linha CONNECT e no SNI dentro do ClientHello, mas pacotes para o IP dele partindo da sua máquina não existem e não podem existir.

É possível, pelo dump, descobrir qual IP externo o proxy usou para acessar o servidor?

Não. Essa informação pertence à zona C. A única forma é consultar, através do proxy, um serviço que retorne o endereço do cliente, ou ver no painel do provedor qual endereço estava ativo naquele momento.

O proxy enviou RST. Então o problema está no proxy?

Não necessariamente. RST do endereço do proxy significa que o túnel foi fechado do lado do proxy, e a causa pode estar no servidor de destino ou na rede atrás do proxy. Olhe o timing: se o RST chegou após um tempo visivelmente maior que o RTT até o proxy depois do seu último pacote, provavelmente o proxy traduziu a reação do servidor. Se o RST chegou em um momento fixo ou após um intervalo exato, é provável que seja política do próprio proxy, por exemplo rotação ou limite.

Como medir o RTT até o proxy pelo dump?

A diferença de tempo entre o SYN seu e o SYN-ACK do proxy no início de qualquer conexão. No Wireshark é possível ativar Statistics > TCP Stream Graphs > Round Trip Time para o fluxo ou usar o campo tcp.analysis.ack_rtt como coluna.

O Wireshark não mostra TLS dentro do túnel, apenas dados TCP. O que fazer?

Clique com o botão direito no pacote após a resposta 200 Connection established, escolha Decode As, na coluna Current escolha TLS para a porta TCP do proxy. Confirme também que os pacotes não estão cortados (-s 0 na captura) e que o dissector HTTP está configurado na porta do proxy, se ela não for padrão: Edit > Preferences > Protocols > HTTP > TCP ports.

Qual a diferença entre essa abordagem e o mitmproxy?

O mitmproxy trabalha no nível da aplicação: ele termina o TLS com um certificado falso, lê e pode alterar requisições HTTP. Para isso o cliente precisa confiar no certificado raiz dele. O tcpdump e o Wireshark trabalham no nível de pacotes e não substituem nada: você vê pacotes reais, flags reais e timings reais, incluindo erros TCP que o mitmproxy esconderia, porque ele mesmo os trataria. Para diagnóstico de quedas, o nível de pacotes é mais honesto. Para ver o conteúdo das requisições, o mitmproxy é mais prático, mas, se quiser, o conteúdo do seu próprio tráfego também pode ser visto no Wireshark via SSLKEYLOGFILE, sem substituição de certificados.

É possível descriptografar no Wireshark o tráfego que o proxy conduz com o servidor?

Não. Você não tem nem os pacotes dessa conexão, nem as chaves dela. O SSLKEYLOGFILE fornece chaves apenas das sessões do seu cliente, e dentro do túnel CONNECT é justamente a sua sessão com o servidor, portanto ela pode ser descriptografada. Mas não existe um TLS separado entre proxy e servidor no caso do CONNECT: o proxy simplesmente repassa seus bytes.

Como entender que o cliente está vazando por fora do proxy?

Capture um dump sem filtro pelo proxy, mas com filtro pela sua interface inteira, e olhe as conexões de saída nas portas 80 e 443 para endereços diferentes do proxy, além de UDP 443 (QUIC) e consultas DNS para resolvedores externos com nomes dos hosts de destino. Filtro Wireshark: (tcp.port == 443 || udp.port == 443) && !(ip.addr == 203.0.113.10). Qualquer correspondência é vazamento de configuração.

Qual deve ser o tamanho do dump para o suporte?

Quanto menor, melhor. Um único fluxo problemático recortado ocupa de 5 a 500 KB. Um arquivo maior que 50 MB o suporte analisará por mais tempo, e metade desse volume será tráfego normal. Recorte os fluxos necessários via tshark ou File > Export Specified Packets no Wireshark.

O que fazer se o tcpdump em máquina virtual mostra pacotes de tamanho maior que o MTU?

É o generic segmentation offload: o kernel entrega ao tcpdump segmentos grandes antes de a placa de rede fatiá-los. Isso não afeta a análise de flags, timings e quedas. Se incomodar, desative o offload durante a captura com o comando ethtool -K eth0 gso off tso off gro off, mas lembre-se de que isso reduzirá o desempenho da rede.

Conclusão

Um dump de tráfego ao trabalhar através de proxy não é sobre espionar conteúdo nem sobre magia. É sobre registrar honestamente o que realmente aconteceu no fio: com precisão de milissegundo e de cada flag. Os logs do cliente dizem que a conexão caiu. O dump diz quem fez isso, quando exatamente e o que houve antes.

Resumindo a metodologia. Na máquina cliente você vê apenas a conexão até o proxy: handshake TCP, diálogo CONNECT ou SOCKS e registros TLS criptografados dentro do túnel. O servidor de destino não é visível diretamente, mas seu comportamento é traduzido pelo proxy através dos códigos de resposta ao CONNECT, dos timings e da forma de fechamento do túnel. RST ou FIN do seu endereço - é problema seu ou seu timeout. Retransmissões e ACKs duplicados sem fechamento - perdas no caminho até o proxy. Fechamento pelo proxy após um tempo múltiplo do RTT até ele - reação traduzida do servidor. Fechamento em momentos fixos - política do proxy. Zero Window de você - sua aplicação não está lendo.

Passos práticos para hoje: coloque nos favoritos os comandos tcpdump com rotação e os filtros Wireshark deste artigo; verifique se os relógios das máquinas onde rodam seus clientes estão sincronizados; certifique-se de que seu cliente não tem vazamento de QUIC por fora do proxy; crie credenciais de teste para diagnóstico, para não expor as de produção nos dumps. E da próxima vez que os logs disserem connection reset by peer, não fique adivinhando. Capture o dump, abra o fluxo, olhe a direção e o tempo. Em cinco minutos você saberá a quem escrever: a si mesmo, ao suporte do proxy ou ao dono do servidor de destino.