tcpdump y Wireshark al trabajar a través de un proxy: captura de tráfico y diagnóstico de cortes
Contenido del artículo
- Introducción: cuando los logs del cliente ya no alcanzan
- Fundamentos: qué se ve antes del proxy, dentro del túnel y después de él
- Inmersión profunda: por qué el proxy no puede mostrar más y cómo leer los indicios indirectos
- Tcpdump en la práctica: filtros, escritura a archivo, rotación
- Wireshark: filtros de visualización, follow tcp stream, lectura de tls sin descifrado
- Diagnóstico por dump: rst, fin, retransmission, zero window
- Descifrado de tu propio tráfico mediante sslkeylogfile
- Cuadros típicos: timeout de conexión, corte a mitad de la respuesta, pérdidas en red móvil
- Errores típicos al capturar y leer un dump
- Herramientas y recursos
- Casos y resultados
- Lista de verificación para capturar un dump antes de contactar al soporte
- Faq
- Conclusión
Cuando una aplicación funciona a través de un proxy y algo sale mal, lo primero que abre un ingeniero son los logs del cliente. Ahí dice algo como connection reset by peer, read timeout o simplemente EOF. El problema es que esas líneas casi no dicen nada sobre la causa. ¿Quién reinició la conexión: el proxy, el servidor destino o el operador de red entre tú y el proxy? ¿Llegó la petición siquiera al proxy? ¿Alcanzó a completarse el handshake TLS? La librería del cliente HTTP no lo sabe, y por lo tanto tú tampoco.
En este artículo bajaremos un nivel, hasta los paquetes. Veremos cómo capturar un dump con tcpdump en la máquina cliente, qué se ve exactamente en ese dump al trabajar a través de un proxy HTTP y SOCKS5, por qué en un túnel HTTPS solo verás el CONNECT y registros TLS cifrados, cómo leer el dump en Wireshark y cómo determinar, por las banderas RST, FIN, las retransmisiones y la ventana cero, de qué lado se cortó la conexión. Hablaremos aparte del descifrado de tu propio tráfico mediante SSLKEYLOGFILE y de cómo preparar correctamente un dump para contactar al soporte de un servicio de proxy, por ejemplo Proxeon.
Aclaración importante: este no es un artículo sobre mitmproxy. Ahí se trata de interceptar y sustituir HTTPS a nivel de aplicación con un certificado falso. Aquí trabajamos a nivel de paquetes, no sustituimos nada ni interceptamos tráfico ajeno. Nuestra tarea es puramente diagnóstica: entender dónde se rompe la cadena cliente - proxy - servidor destino.
Introducción: cuando los logs del cliente ya no alcanzan
Imagina una situación típica. Un script en Python pasa por un proxy móvil, procesa un par de miles de peticiones por hora, y aproximadamente el dos por ciento termina con un error Connection aborted, RemoteDisconnected. El desarrollador agrega reintentos, el error no desaparece. Escribe al soporte del proxy, le piden un ejemplo de petición y la hora. El soporte revisa sus logs y responde: todo está bien por nuestro lado, la conexión al servidor destino se establecía. ¿Quién tiene razón?
Sin un dump esta discusión es infinita. Con un dump se termina en cinco minutos: se ve que el proxy respondió al CONNECT con código 200, el cliente envió el ClientHello, y a los 180 milisegundos llegó un RST desde el proxy. Esto significa que o bien el proxy no logró entenderse con el servidor destino, o bien el servidor cerró la conexión por su cuenta. Después se pueden mirar los tiempos y precisar. Lo importante es que la conversación pasó de las suposiciones a los hechos.
Los logs del cliente HTTP operan a nivel de aplicación. Ven el resultado, pero no el proceso. Un dump de tráfico muestra el proceso: cada paquete con su marca de tiempo exacta, dirección, banderas y tamaño. Por eso, en la operación seria de infraestructura de proxy, saber capturar y leer un dump se considera una habilidad básica, no una excentricidad.
Qué obtendrás de este artículo
- Entender qué datos pasan físicamente por la interfaz de red del cliente al trabajar a través de un proxy y cuáles de ellos se pueden ver en texto claro.
- Comandos listos de tcpdump para grabar un dump con filtros, rotación y límite de tamaño.
- Un conjunto de filtros de visualización de Wireshark que puedes copiar tal cual.
- Una metodología para distinguir un corte del lado propio, del lado del proxy y del lado del servidor destino.
- Una forma segura de descifrar tu propio tráfico TLS para depuración.
- Una lista de verificación para preparar un dump antes de contactar al soporte.
Fundamentos: qué se ve antes del proxy, dentro del túnel y después de él
Empecemos con el esquema. Al trabajar a través de un proxy hay tres tramos del camino, y en la máquina cliente físicamente solo puedes observar el primero.
Tres zonas de observación
- Zona A: cliente - proxy. Es la única conexión TCP que pasa por tu interfaz de red. La ve tcpdump en tu máquina. Aquí se ven: el handshake TCP con la IP del proxy, el protocolo de servicio del proxy (HTTP CONNECT o SOCKS5), y luego, o bien HTTP en claro, o bien registros TLS cifrados.
- Zona B: dentro del túnel. Después de que el proxy responde 200 Connection established, todos los bytes entre tú y el servidor destino simplemente se retransmiten de punta a punta. Si el servidor destino opera por HTTPS, esos bytes son registros TLS. Ves su estructura (tipo de registro, longitud, ClientHello con SNI, ServerHello, alertas), pero no el contenido.
- Zona C: proxy - servidor destino. Es una conexión TCP aparte que el proxy establece en su propio nombre y desde su propia dirección externa. En la máquina cliente no existe en absoluto. Solo se puede ver en el propio servidor proxy, y eso es infraestructura del proveedor. Todo lo que averigües sobre la zona C lo averiguas indirectamente: por los códigos de respuesta al CONNECT, por las latencias y por cómo el proxy cierra el túnel.
Esta es la idea clave de todo el artículo. El dump en el cliente no muestra el servidor destino directamente. Pero sí muestra el comportamiento del proxy, y el proxy, como buen retransmisor, traduce el comportamiento del servidor destino a una forma interpretable. Nuestra tarea es aprender a leer esa traducción.
Proxy HTTP y HTTP en claro
El caso más transparente. Si el sitio destino opera por http sin cifrado, el cliente envía al proxy una petición con URI absoluta:
GET http://example.com/api/items HTTP/1.1
Host: example.com
Proxy-Authorization: Basic dXNlcjpwYXNz
User-Agent: my-client/1.0En el dump se ve todo: método, ruta, encabezados, cuerpo, la respuesta del proxy con el cuerpo del servidor. Fíjate en el encabezado Proxy-Authorization. Son tus credenciales en base64, y están en el dump en texto claro. Anotemos este hecho, nos servirá cuando hablemos de pasar el dump a terceros.
Proxy HTTP y HTTPS mediante CONNECT
Aquí empieza lo más interesante. Para HTTPS el cliente primero le pide al proxy establecer un túnel TCP hasta el host y puerto:
CONNECT api.example.com:443 HTTP/1.1
Host: api.example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz
Proxy-Connection: Keep-AliveEl proxy responde:
HTTP/1.1 200 Connection establishedA partir de ese momento el proxy deja de analizar HTTP. Simplemente copia bytes de un socket a otro. El cliente inicia el handshake TLS directamente con el servidor destino, y todo el tráfico HTTP (GET, POST, encabezados, cuerpos) queda dentro de TLS. Por eso en el dump de un túnel HTTPS solo se ve el CONNECT: es lo último que el cliente le dice al proxy en texto claro. Después vienen registros TLS, que el proxy no puede leer y tú tampoco en el dump.
Lo que sí queda visible dentro del túnel:
- ClientHello: versiones de TLS, conjunto de cifrados, extensiones y, por regla general, SNI (el nombre del host en texto claro).
- ServerHello: versión y cifrado elegidos.
- En TLS 1.2, el certificado del servidor en texto claro. En TLS 1.3 el certificado ya va cifrado.
- TLS Alert: el tipo de alerta es visible en TLS 1.2; en TLS 1.3 las alertas posteriores al handshake van cifradas, pero el hecho mismo de un registro tipo Alert de 2 bytes se reconoce.
- Tamaños y tiempos de los registros Application Data. Con ellos se puede estimar cuántos datos llegaron antes de que se cortara la conexión.
SOCKS5
SOCKS5 funciona distinto, pero la idea es la misma. El cliente envía el saludo con métodos de autenticación (bytes 05 02 00 02), el proxy elige el método, luego viene la autenticación por usuario y contraseña (subprotocolo con bytes 01, longitud, usuario, longitud, contraseña), luego el comando CONNECT con tipo de dirección 03 (nombre de dominio) y puerto. El proxy responde 05 00 si tuvo éxito o con un código de error: 01 error general, 03 red inalcanzable, 04 host inalcanzable, 05 conexión rechazada, 06 TTL agotado. Estos códigos de error son la traducción directa de lo que el proxy vio en la zona C. Wireshark sabe decodificar SOCKS si indicas el puerto mediante Decode As.
Qué no se ve nunca
Desde la máquina cliente no verás: la IP externa del proxy con la que sale al servidor destino (para proxies móviles es la dirección del operador), la conexión TCP proxy - servidor, ni las consultas DNS del proxy. Si el soporte de Proxeon dice que ve un error del lado del servidor destino, está mirando precisamente la zona C, inaccesible para ti. Tu tarea es traer el dump de la zona A para contrastar ambas imágenes por tiempo.
Inmersión profunda: por qué el proxy no puede mostrar más y cómo leer los indicios indirectos
Vale la pena entender por qué la imagen es así y qué se deduce de ello para el diagnóstico.
El túnel CONNECT como tubería de bytes
Tras responder 200, el proxy HTTP, según la especificación, está obligado a transmitir bytes en ambos sentidos sin interpretarlos hasta que una de las partes cierre. No sabe que dentro hay TLS. No sabe qué peticiones HTTP envías. Solo ve dos eventos: un flujo de bytes y el cierre del socket. Cuando el servidor destino cierra la conexión con el proxy (envía FIN o RST), el proxy está obligado a cerrar la conexión contigo. Cómo lo haga exactamente depende de la implementación: unos proxies traducen FIN como FIN y RST como RST, otros lo convierten todo en FIN, y otros, ante un error de lectura del socket remoto, envían un RST al cliente.
Consecuencia práctica: un RST desde la dirección IP del proxy no significa que el proxy tenga la culpa. Significa que el túnel se cerró del lado del proxy, y la causa puede estar en cualquier lugar detrás de él. Para entender la causa, miramos el contexto: qué pasó antes del RST, cuánto tiempo transcurrió, si llegó alguna respuesta.
Diferencia entre un SYN al proxy y un SYN al servidor destino
Es la fuente más frecuente de confusión. En un dump a través de un proxy nunca verás un SYN al puerto 443 del servidor destino. Todos los SYN van a la IP y puerto del proxy. Si el SYN al proxy no recibe SYN-ACK y se repite con intervalos de 1, 2, 4, 8 segundos, el problema está entre tú y el proxy: la red, un firewall, una dirección o puerto incorrectos, el proxy no está levantado. El servidor destino no tiene nada que ver aquí, ni siquiera intentaste contactarlo en términos de tu dump.
En cambio, si el TCP con el proxy está establecido, el CONNECT se envió, y la respuesta llega tras 20-30 segundos con código 504 Gateway Timeout o 502 Bad Gateway, el proxy no pudo establecer la zona C. El tiempo aquí habla por sí solo: tu paquete CONNECT salió al instante, la respuesta tardó mucho, o sea que el proxy esperó un timeout en su intento.
TLS 1.3, ECH y qué cambia hacia 2026
El panorama se va cerrando poco a poco. TLS 1.3 domina, y en él el certificado del servidor va cifrado, así que verificar qué certificado devolvió el servidor a partir del dump sin claves ya no es posible. La extensión ECH (Encrypted Client Hello) se va implementando gradualmente en navegadores y grandes CDN, y ahí el SNI también se cifra. Para el diagnóstico a través de un proxy esto es menos crítico, porque el nombre del host de todas formas se ve en la línea CONNECT, pero el filtro habitual por SNI se activará con menos frecuencia.
Otra tendencia es HTTP/3 sobre QUIC. Un proxy HTTP clásico con CONNECT solo tuneliza TCP. Si de repente en el dump ves tráfico UDP al puerto 443 directamente hacia la dirección del servidor destino, saltándose el proxy, es señal de una fuga: la aplicación intenta usar QUIC directamente, no a través del proxy. Un cliente bien configurado debería o bien desactivar QUIC al trabajar con proxy, o bien usar proxificación UDP mediante mecanismos aparte. Filtro para una verificación rápida: udp.port == 443. Si el dump a través del proxy muestra esos paquetes, hay que corregir la configuración del cliente.
Dónde colocar el punto de captura
Normalmente la respuesta es una sola: en la máquina cliente. Pero hay matices. Si el cliente corre en un contenedor Docker, tcpdump en el host sobre la interfaz docker0 o sobre la interfaz del puente mostrará el tráfico antes del NAT, y sobre la interfaz externa lo mostrará después del NAT, con otra dirección de origen. Es más simple entrar al espacio de red del contenedor: nsenter -t PID -n tcpdump ... o lanzar un contenedor con tcpdump mediante --net=container:nombre. En máquinas virtuales en la nube, presta atención al offloading: los paquetes en el dump pueden verse más grandes que el MTU porque la tarjeta de red fusiona segmentos. Eso es normal y no afecta al análisis de banderas.
tcpdump en la práctica: filtros, escritura a archivo, rotación
tcpdump está en casi cualquier máquina Linux y en macOS. En Windows, un rol equivalente lo cumple Wireshark con el driver Npcap o el dumpcap de consola. Veamos los comandos que cubren el 95 por ciento de las tareas de diagnóstico de proxy.
Captura básica de tráfico hacia el proxy
Supongamos que la dirección del proxy es 203.0.113.10, puerto 8080. Grabamos todo lo que va entre nosotros y el proxy en un archivo:
sudo tcpdump -i any -nn -s 0 -w proxy.pcap 'host 203.0.113.10 and port 8080'Análisis de las opciones:
- -i any - escuchar todas las interfaces. Cómodo cuando no sabes por cuál sale el tráfico. Si lo sabes, mejor indicar una concreta: -i eth0 o -i wlan0. En macOS -i any no se admite, indica en0.
- -nn - no resolver IP a nombres ni puertos a nombres de servicio. La resolución ralentiza la captura y agrega consultas DNS innecesarias a la red.
- -s 0 - capturar el paquete completo. En versiones modernas es el valor por defecto, pero indicarlo explícitamente no hace daño.
- -w proxy.pcap - escribir a archivo en formato pcap, no mostrar en pantalla. Solo así se puede abrir luego el dump en Wireshark.
- Filtro entre comillas simples - es un filtro BPF de captura. Descarta todo lo superfluo ya en el kernel, así que el archivo contendrá solo lo necesario.
Filtros por host y puerto
Varios proxies o un rango de puertos:
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and portrange 10000-10100'Proxy por nombre de dominio (tcpdump lo resolverá una sola vez al arrancar, algo que no siempre conviene con direcciones rotativas):
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host proxy.example.net and port 8080'Capturar solo paquetes de servicio sin datos, para ver handshakes y cortes cuando el volumen de tráfico es alto:
sudo tcpdump -i eth0 -nn -w flags.pcap 'host 203.0.113.10 and tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'Solo RST en ambos sentidos:
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'Captura excluyendo tu propia sesión SSH, para no ensuciar el dump cuando lo tomas en un servidor remoto:
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and not port 22'Cómo no llenar gigabytes: rotación por tamaño y tiempo
Si el error es raro y se reproduce una vez por hora, el dump debe girar mucho tiempo. Sin rotación el disco se acaba. Rotación por tamaño, 100 MB por archivo, anillo de 10 archivos:
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'Aquí -C fija el tamaño en millones de bytes, -W limita la cantidad de archivos: el undécimo archivo sobrescribirá el primero. Rotación por tiempo, archivo nuevo cada 10 minutos, conservar los últimos 24 archivos (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'La opción -G indica el intervalo en segundos, y el patrón de tiempo en el nombre del archivo es obligatorio; de lo contrario tcpdump sobrescribirá un solo archivo. Una opción útil, -Z usuario, descarta privilegios tras abrir la interfaz, y -U obliga a escribir paquetes al archivo de inmediato, sin búfer, lo cual es importante si vas a leer el archivo en paralelo o temes perder la cola ante una finalización anómala.
Recorte de la carga útil
Otra forma de reducir volumen es capturar solo encabezados. Para diagnosticar cortes no hace falta el contenido de los registros TLS; basta con los primeros 128 bytes de cada paquete:
sudo tcpdump -i eth0 -nn -s 128 -w headers.pcap 'host 203.0.113.10'Cuidado: con -s 128 la línea CONNECT y los encabezados pueden quedar recortados, y Wireshark marcará los paquetes como truncated. Para un análisis completo del handshake TLS eso es poco; el ClientHello suele ocupar 300-600 bytes y más. Un punto intermedio es -s 600.
Leer el dump directamente en consola
Wireshark no siempre está a mano, y hay que echar un vistazo rápido. Leer el archivo mostrando banderas TCP y números de secuencia absolutos:
tcpdump -nn -r proxy.pcap -S -tttt 'tcp[tcpflags] & (tcp-rst|tcp-fin) != 0'La opción -tttt imprime la fecha y hora completas, -S muestra números de secuencia absolutos, lo que ayuda a cotejar con los logs. Volcar el contenido de los paquetes en ASCII para ver la línea CONNECT y la respuesta del proxy:
tcpdump -nn -r proxy.pcap -A 'port 8080' | grep -E 'CONNECT|HTTP/1'tshark como Wireshark de consola
Si Wireshark está instalado en el servidor, tshark da acceso a los mismos disectores desde consola. Lista de todos los CONNECT con códigos de respuesta:
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.codeLista de flujos terminados en RST, indicando quién lo envió:
tshark -r proxy.pcap -Y 'tcp.flags.reset == 1' -T fields -e frame.time_relative -e tcp.stream -e ip.src -e ip.dstWireshark: filtros de visualización, Follow TCP Stream, lectura de TLS sin descifrado
Abriste el pcap en Wireshark y ves miles de líneas. ¿Por dónde empezar? Por los filtros de visualización. A diferencia de los filtros BPF de captura, estos operan sobre el archivo ya grabado y entienden la estructura de los protocolos.
Filtros básicos para trabajar a través de un proxy
Todo lo relacionado con el proxy:
ip.addr == 203.0.113.10 && tcp.port == 8080Todas las peticiones CONNECT:
http.request.method == "CONNECT"CONNECT a un host concreto:
http.request.method == "CONNECT" && http.host contains "api.example.com"Respuestas del proxy distintas de 200 (errores de autorización 407, indisponibilidad 502, timeouts 504):
http.response.code >= 400 && tcp.port == 8080Solo respuestas 407, señal de credenciales incorrectas o límite agotado:
http.response.code == 407Filtros TLS dentro del túnel
Wireshark sabe reconocer que después del CONNECT con respuesta 200 empieza TLS, y coloca el disector TLS automáticamente. Si no lo reconoce (pasa con paquetes recortados), haz clic derecho en el paquete, elige Decode As e indica TLS para ese puerto.
Todos los ClientHello:
tls.handshake.type == 1ClientHello con un SNI concreto:
tls.handshake.extensions_server_name contains "example.com"ServerHello (si no aparece después del ClientHello, el handshake no arrancó del lado del servidor):
tls.handshake.type == 2Alertas TLS:
tls.alert_messageEncontrar un flujo que tenga ClientHello pero no ServerHello con un solo filtro es más difícil; es más cómodo ir a Statistics > Conversations, ordenar por cantidad de paquetes y mirar los flujos con 5-7 paquetes.
Filtros por problemas de TCP
Todos los paquetes con RST:
tcp.flags.reset == 1RST enviados por el proxy hacia nosotros:
tcp.flags.reset == 1 && ip.src == 203.0.113.10RST enviados por nosotros:
tcp.flags.reset == 1 && ip.dst == 203.0.113.10Retransmisiones:
tcp.analysis.retransmissionVentana cero y sus consecuencias:
tcp.analysis.zero_window || tcp.analysis.window_fullTodas las anomalías que Wireshark notó por sí mismo (retransmisiones, ACK duplicados, segmentos perdidos, ventana cero):
tcp.analysis.flags && !tcp.analysis.window_updateSYN sin respuesta, es decir, SYN repetidos:
tcp.flags.syn == 1 && tcp.flags.ack == 0 && tcp.analysis.retransmissionPausas grandes dentro del flujo, más de 5 segundos entre paquetes:
tcp.time_delta > 5Para este filtro hay que activar Edit > Preferences > Protocols > TCP > Calculate conversation timestamps.
Follow TCP Stream
Encontraste un paquete sospechoso, por ejemplo un RST. Haces clic derecho > Follow > TCP Stream. Wireshark mostrará todo el diálogo de esa conexión como texto: nuestro tráfico en un color, el del proxy en otro. Para HTTPS mediante CONNECT verás la línea CONNECT, la respuesta 200 Connection established, y después bytes TLS ilegibles. Eso es normal. Hay que mirar los volúmenes: cuántos bytes salieron de nosotros (nuestro ClientHello y peticiones), cuántos llegaron (ServerHello y respuesta) y dónde terminó todo.
Abajo en la ventana Follow hay contadores: tantos bytes cliente, tantos bytes servidor. Si del servidor llegaron 0 bytes tras la respuesta 200, el servidor destino no respondió al ClientHello en absoluto. Si llegaron entre 100 y 4000 bytes y se cortó, el handshake empezó pero no se completó. Si llegaron decenas de kilobytes y luego un corte, el problema está en medio de la transferencia de datos.
Un hábito útil: filtra por número de flujo. Tras Follow, en la barra de filtro aparecerá automáticamente tcp.stream eq 42. Cierra la ventana Follow y en la lista principal quedará solo ese flujo con banderas y tiempos. Es la vista más cómoda para analizar el corte.
Leer el handshake TLS sin descifrado
Despliega el ClientHello en el árbol del paquete. Qué mirar:
- Version y supported_versions - ¿ofrece el cliente TLS 1.3? Si el servidor exige 1.3 y el cliente solo ofrece 1.2, el servidor cerrará la conexión con una alerta protocol_version o simplemente con FIN.
- server_name - ¿coincide el SNI con el host del CONNECT? Una discrepancia suele deberse a una mala configuración del cliente y provoca errores de certificado.
- Cipher Suites - lista de cifrados. Una lista demasiado corta u obsoleta es causa de la alerta handshake_failure.
- ALPN - ¿ofrece el cliente h2? Si el servidor eligió h2 y el cliente internamente esperaba HTTP/1.1, la aplicación puede caerse con errores incomprensibles.
En ServerHello mira la versión y el cifrado elegidos. En TLS 1.2 después viene el Certificate en texto claro, se puede verificar el nombre y la fecha de vencimiento. En TLS 1.3 tras el ServerHello casi todo va cifrado, y lo siguiente que se lee es el tipo de registro. Application Data significa que el handshake se completó con éxito y empezaron los datos. Un Alert de 2 bytes justo después del ServerHello o en su lugar significa que algo va mal: en TLS 1.3 no verás el código de alerta sin claves, pero el hecho mismo ya es informativo.
En Statistics > Conversations > TCP es cómodo evaluar el panorama general: cuántos flujos, cuántos bytes en cada dirección, duración. Flujos con una duración de 0.05 segundos y 6 paquetes son, muy probablemente, conexiones reiniciadas justo después del handshake.
Diagnóstico por dump: RST, FIN, retransmission, zero window
Ahora lo principal. ¿Cómo se entiende, por el dump de la zona A, dónde se cortó exactamente? Veamos cada señal y su interpretación en el contexto de un proxy.
Matriz de direcciones
La primera pregunta siempre es la misma: ¿quién envió el paquete de cierre? En el dump eso es el campo ip.src. Solo hay dos opciones: nuestra dirección o la del proxy.
- RST o FIN desde nuestro lado - la conexión la cerró nuestra aplicación, nuestro SO o algo en nuestro host. El proxy y el servidor destino no participaron. Causas típicas: timeout del cliente HTTP, finalización anómala del proceso, agotamiento de descriptores de archivo, acción de un firewall o antivirus local.
- RST o FIN desde el proxy - el túnel se cerró del lado del proxy. La causa está o en el propio proxy (límites, política, timeout de inactividad), o traducida desde el servidor destino, o desde la red entre el proxy y el servidor. Se distingue por el contexto y los tiempos.
- Ni RST ni FIN, solo retransmisiones - los paquetes se pierden en el camino entre nosotros y el proxy. Ninguna de las partes cerró la conexión, simplemente murió por pérdidas.
RST después del CONNECT sin respuesta 200
Cuadro: TCP establecido, enviamos el CONNECT, el proxy respondió con RST sin respuesta HTTP. Este comportamiento suele significar que el proxy rechazó a nivel de política o no pudo interpretar la petición. Causas: puerto destino no permitido, límite de conexiones simultáneas, formato de petición incorrecto. Aquí el problema está en la zona A o en el propio proxy. El servidor destino no tiene nada que ver.
Respuesta 502, 503 o 504 al CONNECT
Cuadro: el CONNECT salió, tras N segundos llegó una respuesta HTTP con código 5xx. Miremos N. Si la respuesta llegó en un tiempo del orden del RTT hasta el proxy, el proxy rechazó de inmediato: quizá el nombre DNS no resuelve de su lado o la dirección es inalcanzable al instante (ICMP unreachable). Si N es de unos 10-30 segundos, el proxy esperó un timeout al conectar con el servidor destino: el servidor no responde al SYN. En ambos casos es la zona C, y tu dump demuestra que hiciste todo bien y que el proxy informó honestamente de la imposibilidad.
FIN o RST justo después del ClientHello
Cuadro: CONNECT - 200 - nuestro ClientHello - tras un RTT hasta el proxy más algo llega un FIN o RST desde el proxy. Aquí hay dos candidatos: el servidor destino rechazó la conexión en la etapa TLS (no le gustó el SNI, la versión, la falta de certificado de cliente) o un sistema de protección del servidor reinició la conexión por características del ClientHello. El indicio clave es el tiempo. Si entre nuestro ClientHello y el corte pasó un tiempo notablemente mayor que el RTT hasta el proxy, entonces el proxy alcanzó a enviar el ClientHello hacia adelante y recibió una reacción. No fue el proxy quien decidió cerrar, fue una reacción del servidor destino traducida.
Compara con el RTT hasta el proxy, que mides por el handshake TCP: el tiempo entre SYN y SYN-ACK. Si el RTT hasta el proxy es 40 ms y el corte tras el ClientHello llegó a los 200 ms, la diferencia de 160 ms es aproximadamente el RTT proxy - servidor de ida y vuelta. El cuadro es del todo consistente con un rechazo del servidor.
Corte después del ServerHello o tras parte de los datos
El handshake empezó, el servidor respondió, empezaron los datos, y a mitad de camino un FIN o RST. Si es un FIN y viene del proxy, y antes el último registro Application Data tiene un tamaño lógico, quizá el servidor simplemente cerró la conexión tras la respuesta (Connection: close dentro de TLS), y el cliente lo interpretó mal. Si es un RST en medio del flujo de datos sin una caída previa del ritmo, es o un cierre forzado en el servidor, o el proxy interrumpió el túnel por límite de tráfico o tiempo de vida de la sesión. Para proxies móviles con rotación de IP por tiempo, lo segundo es muy probable: el corte ocurre justo en el momento del cambio de IP. Verifica si la hora del corte coincide con el intervalo de rotación en la configuración de tu proxy.
Retransmisiones y su dirección
Wireshark marca un paquete como retransmission cuando ve la retransmisión de los mismos números de secuencia. Miremos quién retransmite:
- Retransmitimos nosotros - nuestros paquetes no son confirmados por el proxy. O se pierden en el camino hacia el proxy, o se pierden las confirmaciones en el camino de vuelta. En ambos casos el problema está en la red de la zona A.
- Retransmite el proxy - sus paquetes no son confirmados por nosotros. No los recibimos o nuestros ACK no llegan. También es zona A, pero con sesgo hacia la dirección entrante.
- Retransmisiones de SYN - caso aparte: el proxy es inalcanzable a nivel TCP, la conexión no se establece en absoluto.
Importante: las pérdidas en la zona C no las verás nunca como retransmisiones. El proxy se entiende con el servidor destino por su cuenta. El único rastro de pérdidas en la zona C son las pausas: el proxy nos transmite datos de forma desigual, con huecos, aunque no haya retransmisiones en el dump. El filtro tcp.time_delta > 1 para paquetes desde el proxy mostrará esas pausas.
Zero Window
Una ventana TCP de tamaño cero significa que el receptor no alcanza a leer los datos del búfer del socket. Si la ventana cero la anunciamos nosotros, nuestra aplicación no lee la respuesta con suficiente rapidez: hilo ocupado, bloqueo, procesamiento lento. El proxy en ese caso espera, luego envía un Zero Window Probe, y si la aplicación no empieza a leer, tras varias decenas de segundos puede cerrar la conexión. El cliente verá un corte y culpará al proxy, aunque la causa esté en el cliente.
Si la ventana cero la anuncia el proxy, significa que no alcanza a transmitir nuestros datos hacia adelante: el servidor destino recibe despacio. Es un indicio indirecto de problemas en la zona C, se ve al descargar archivos grandes a través de un proxy hacia un servidor lento.
ACK duplicados y SACK
Los ACK duplicados del proxy son señal de que recibió un paquete fuera de orden, algo nuestro se perdió. Muchos ACK duplicados y retransmisiones posteriores es el cuadro clásico de pérdidas en una red móvil o Wi-Fi de nuestro lado. Los ACK duplicados desde nuestro lado indican que se perdieron paquetes del proxy. La presencia de la opción SACK en el handshake permite a TCP recuperarse con mayor eficiencia, pero el hecho de las pérdidas igual queda a la vista.
Heurística del TTL
Técnica avanzada. Mira el campo IP TTL en los paquetes normales del proxy y en el paquete RST. Si el TTL del RST difiere en varias unidades, el RST no lo generó el propio proxy, sino un nodo intermedio del camino: un firewall, un balanceador o un sistema de filtrado del operador. No es una prueba absoluta, pero es un indicio fuerte de que hay que revisar el camino entre tú y el proxy, y no culpar al proxy ni al servidor destino.
Tabla resumen de interpretaciones
- SYN se repite, no hay SYN-ACK: el proxy es inalcanzable o la red hasta él. Zona A.
- SYN - RST: el puerto del proxy está cerrado o filtrado. Zona A.
- CONNECT - 407: autorización incorrecta o límite de la cuenta. Proxy.
- CONNECT - 502/504 rápido: el proxy no pudo iniciar la conexión con el servidor. Zona C, probablemente DNS o ruta.
- CONNECT - 504 tras 20-30 segundos: el servidor no responde al SYN del proxy. Zona C.
- 200 - ClientHello - RST/FIN tras un tiempo mayor que el RTT: el servidor rechazó TLS. Zona C.
- 200 - ClientHello - silencio - corte por timeout del cliente: el servidor acepta TCP, pero no responde a TLS. Zona C o un filtro bloqueante detrás del proxy.
- Los datos fluyen - RST del proxy en un momento fijo: límite o rotación del proxy. Proxy.
- Retransmisiones y ACK duplicados sin RST: pérdidas en la red entre nosotros y el proxy. Zona A.
- Zero Window de nuestro lado: nuestra aplicación no lee. Cliente.
- RST de nuestro lado tras un silencio prolongado: nuestro timeout. Cliente.
Descifrado de tu propio tráfico mediante SSLKEYLOGFILE
A veces las banderas no alcanzan y hay que ver qué respondió exactamente el servidor dentro de TLS: el código de respuesta, los encabezados, el cuerpo del error. Para esto no hace falta mitmproxy ni un certificado falso. Basta con que tu propio cliente escriba las claves de sesión en un archivo y que Wireshark las use para descifrar. Esto funciona solo para tu cliente y tus conexiones: las claves solo las tiene la parte que participó en el handshake. Descifrar tráfico ajeno, o el tráfico que el proxy lleva con el servidor en la zona C, de esta forma es imposible, y así está bien.
Cómo activarlo en distintos clientes
Los navegadores basados en Chromium y Firefox leen la variable de entorno SSLKEYLOGFILE y escriben allí las claves en formato NSS Key Log:
export SSLKEYLOGFILE=/home/user/tls-keys.log
google-chrome --proxy-server=http://203.0.113.10:8080curl, compilado con OpenSSL, también lee esa variable:
export SSLKEYLOGFILE=/home/user/tls-keys.log
curl -v -x http://user:pass@203.0.113.10:8080 https://api.example.com/healthNode.js con la opción de línea de comandos:
node --tls-keylog=/home/user/tls-keys.log app.jsPython no lee la variable automáticamente, pero desde la versión 3.8 el SSLContext tiene el atributo keylog_filename. Para requests mediante un 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 mediante el campo KeyLogWriter en 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,
}Conectar las claves en Wireshark
Dos formas. La primera: Edit > Preferences > Protocols > TLS > (Pre)-Master-Secret log filename, indicar la ruta al archivo de claves. Wireshark descifrará todos los flujos para los que encuentre coincidencia por Client Random. La segunda forma es más confiable para pasar el archivo a colegas: incrustar las claves directamente en el pcapng:
editcap --inject-secrets tls,/home/user/tls-keys.log proxy.pcap proxy-with-keys.pcapngTras esto, en Wireshark, dentro del túnel CONNECT aparecerán peticiones y respuestas HTTP/1.1 o HTTP/2 descifradas. Filtro para HTTP/2 descifrado:
http2.header.name == ":status"Para HTTP/1.1 descifrado dentro del túnel funcionan los filtros habituales http.response.code y http.request.uri.
Qué aporta esto al diagnóstico a través de un proxy
El descifrado elimina la última incertidumbre. Ves que el servidor respondió 429 con el encabezado Retry-After y luego cerró la conexión, o que devolvió 200 pero el cuerpo se cortó en el 40 por ciento, o que la petición salió y no hubo respuesta alguna. Con ese cuadro, contactar al soporte del proxy se vuelve concreto: o el problema está claramente en el servidor, o claramente en el túnel.
Reglas de seguridad
- El archivo de claves permite descifrar por completo las sesiones grabadas, incluidas cookies y tokens. Guárdalo como una contraseña y bórralo después del análisis.
- No pases el archivo de claves junto con el dump al soporte del proxy ni a nadie más. Para analizar cortes, el soporte no necesita las claves, le bastan banderas y tiempos.
- Si de todas formas hay que mostrar contenido descifrado, hazlo con una cuenta de prueba en el servicio destino y credenciales de proxy de prueba.
- No dejes SSLKEYLOGFILE activado en producción. Una variable de entorno que acabe por error en la configuración de un servicio estará años escribiendo claves al disco.
Cuadros típicos: timeout de conexión, corte a mitad de la respuesta, pérdidas en red móvil
Juntemos los indicios analizados en escenarios reconocibles. Cada uno se describe tal como se ve en la lista de paquetes de Wireshark tras el filtro tcp.stream eq N.
Cuadro 1: timeout de conexión con el 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)Ni un solo paquete del proxy. Los intervalos exponenciales 1, 2, 4, 8 segundos son el retransmission backoff estándar del kernel de Linux. Diagnóstico: el proxy es inalcanzable desde tu punto. Revisa dirección, puerto, firewall, enrutamiento, y también si la dirección del proxy cambió. Si es un proxy móvil Proxeon con puerto dedicado, asegúrate de que el puerto de tu panel de usuario coincide con el que está en la configuración del cliente.
Cuadro 2: timeout del proxy al conectar con el servidor 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 hasta el proxy 41 ms, el proxy confirmó la recepción del CONNECT, y después estuvo 30 segundos en silencio y devolvió 504. Diagnóstico: el servidor destino no responde al proxy en su intento de conexión. Causas posibles: el servidor está caído, el puerto está cerrado para el pool de direcciones del proxy, problemas de red en la ruta proxy - servidor. Tu cliente y tu red no tienen nada que ver.
Cuadro 3: el servidor rechaza 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]Corte 164 ms después del ClientHello con un RTT hasta el proxy de 45 ms. La diferencia de unos 120 ms es el tiempo que tardó el proxy en reenviar el ClientHello al servidor y recibir el cierre. Diagnóstico: el servidor aceptó TCP, pero cerró la conexión en la etapa TLS. Revisa los parámetros del handshake: versiones, cifrados, SNI, ALPN. Si no hay ServerHello ni alerta, el servidor cerró la conexión en silencio, algo que suelen hacer los sistemas de protección ante un ClientHello atípico.
Cuadro 4: corte a mitad de la respuesta
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)
... ещё 340 пакетов данных
4.812 proxy -> client Application Data (1460 bytes)
4.813 proxy -> client [RST, ACK]El handshake pasó, llegaron unos 500 KB de datos, y un RST sin desaceleración, sin retransmisiones, sin zero window. Mira el tiempo absoluto. Si coincide con el momento de rotación de IP en el proxy móvil o con el vencimiento del límite de duración de sesión, la causa está en el proxy, y la solución es alinear el intervalo de rotación con la duración de tus peticiones o usar el modo sin rotación durante las descargas largas. Si no hay coincidencia, es probable un corte del lado del servidor o de la CDN. Aquí ayuda el descifrado mediante SSLKEYLOGFILE: si dentro se ve el encabezado Content-Length y el cuerpo llegó incompleto, el servidor interrumpió la transferencia.
Cuadro 5: pérdidas en la red móvil del lado del 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)ACK duplicados del proxy, seguidos de retransmisiones nuestras con intervalos crecientes. Ni RST ni FIN. Diagnóstico: pérdidas en el camino cliente - proxy. Si el cliente está conectado por red celular o Wi-Fi, es esperable en momentos de traspaso entre estaciones base. La solución es aumentar los timeouts del cliente, activar TCP keepalive, no mantener conexiones largas inactivas, porque el NAT de los operadores borra los registros de conexiones idle, normalmente a los 30-300 segundos, y el siguiente paquete se va al vacío. Filtro para evaluar la magnitud de las pérdidas:
tcp.analysis.retransmission && ip.dst == 203.0.113.10Cuenta el porcentaje de esos paquetes sobre el total con Statistics > Capture File Properties. Uno o dos por ciento es tolerable para una red móvil; más de cinco, busca el problema en las condiciones de radio o en el equipamiento.
Cuadro 6: nuestro propio 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]La petición salió, el proxy la confirmó, la respuesta no llegó, y exactamente 10 segundos después enviamos el FIN. Es nuestro read timeout. El error en el log del cliente se verá como un corte, pero por el dump se ve: la conexión la cerramos nosotros mismos, porque el servidor tardó más de lo que estamos dispuestos a esperar. La solución es aumentar el timeout o averiguar por qué el servidor es lento, pero el proxy aquí simplemente esperó con nosotros con toda honestidad.
Errores típicos al capturar y leer un dump
Enumeremos aquellos en los que tropiezan con regularidad hasta los ingenieros con experiencia.
- Capturar un dump sin filtro en una máquina cargada. En un minuto se juntan gigabytes, y buscar en ellos los 200 paquetes que necesitas es incómodo. Filtra siempre por el host del proxy.
- Filtrar por la dirección del servidor destino. A través de un proxy, esos paquetes no existen en tu máquina. El filtro devolverá vacío, y uno decide que el tráfico no fluye. Sí fluye, pero hacia el proxy.
- Confundir el filtro de captura con el de visualización. La sintaxis BPF de tcpdump (host, port, tcp[tcpflags]) y la de Wireshark (ip.addr, tcp.port, tcp.flags.reset) son distintas. Un filtro de Wireshark en tcpdump dará error de sintaxis, y viceversa.
- Ver un RST del proxy y culpar de inmediato al proxy. Un RST desde la dirección del proxy es un cierre del túnel, no una admisión de culpa. Mira los tiempos y lo que había antes del RST.
- Ignorar el RTT. Sin la latencia medida hasta el proxy es imposible distinguir un rechazo instantáneo del proxy de un rechazo del servidor traducido.
- Capturar con -s 64 y luego intentar leer el CONNECT. Los encabezados quedarán recortados. Para diagnosticar un proxy hace falta captura completa o al menos -s 600.
- No sincronizar la hora. Si el reloj de la máquina con el dump va atrasado un minuto, no se podrá cotejar el dump con los logs del soporte. Activa NTP e indica UTC en la comunicación.
- Enviar un dump con credenciales del proxy. El encabezado Proxy-Authorization en una petición HTTP en claro al proxy contiene usuario y contraseña. O captura con datos de prueba, o cambia la contraseña después de enviarlo.
- Enviar el archivo de claves junto con el dump. Eso revela todo el contenido de las sesiones. Para analizar cortes el soporte no necesita claves.
- Mantener un solo dump largo sin rotación. El disco se llenará en el peor momento, y tcpdump caerá junto con los paquetes que necesitas.
- No verificar la fuga de QUIC. UDP al 443 directamente es señal de que parte del tráfico va por fuera del proxy, y el diagnóstico por el túnel TCP no mostrará nada.
- Olvidar el segmentation offload. Paquetes de 60 KB en el dump en una máquina virtual no significan un MTU incorrecto. Es el kernel entregando a tcpdump segmentos aún no divididos.
Herramientas y recursos
El conjunto mínimo para trabajar con la metodología descrita.
Captura
- tcpdump - estándar en Linux y macOS. Está instalado en casi todas partes, requiere root o la capacidad CAP_NET_RAW.
- dumpcap - capturador de consola del paquete Wireshark, admite las mismas opciones de rotación (-b filesize, -b files) y el formato pcapng.
- Wireshark con Npcap - para Windows. Se puede capturar directamente desde la GUI con filtro de captura en la misma sintaxis BPF.
- tshark - análisis de consola con los disectores de Wireshark, cómodo en servidores sin gráficos y para automatización.
Análisis
- Wireshark - la herramienta principal. Follow TCP Stream, Expert Info, Statistics > Conversations, IO Graph para visualizar pausas y picos de retransmisiones.
- editcap - corte y filtrado de pcap, incrustación de claves TLS en pcapng.
- mergecap - unión de archivos de rotación en uno solo para el análisis.
- capinfos - resumen rápido del archivo: duración, cantidad de paquetes, tamaño.
Clientes con soporte de depuración
- curl con las opciones -v y --trace-time duplica el cuadro del dump a nivel de aplicación y admite SSLKEYLOGFILE.
- openssl s_client -proxy host:port permite ejecutar manualmente el CONNECT y el handshake TLS a través del proxy y ver la respuesta del servidor sin cliente HTTP.
One-liners útiles
Unir los archivos de rotación:
mergecap -w all.pcapng proxy-*.pcapExtraer del dump un solo flujo para enviarlo al soporte:
tshark -r all.pcapng -Y 'tcp.stream eq 42' -w stream42.pcapngEstadísticas rápidas de flujos con RST:
tshark -r all.pcapng -q -z conv,tcp | head -40Verificación manual del CONNECT con openssl:
openssl s_client -proxy 203.0.113.10:8080 -connect api.example.com:443 -servername api.example.com -briefCasos y resultados
Caso 1: dos por ciento de cortes, el culpable resultó ser el cliente
Un servicio de recolección de precios trabajaba a través de un pool de proxies móviles, unas 40 mil peticiones al día. Aproximadamente el 2,3 por ciento de las peticiones caían con RemoteDisconnected. El equipo estaba seguro de que la culpa era de los proxies. Capturaron un dump con rotación de 100 MB durante un día, filtraron tcp.flags.reset == 1 y miraron ip.src. En el 91 por ciento de los casos el RST lo enviaba el propio cliente. El análisis de flujos mostró: antes del RST había exactamente 5 segundos de silencio tras enviar la petición, y luego el RST del cliente. En el código había un read timeout de 5 segundos, y el servidor destino en horas pico respondía en 6-8 segundos. Aumentar el timeout a 15 segundos redujo los errores al 0,3 por ciento. El 0,3 por ciento restante eran FIN reales del proxy, entre 160 y 200 ms después del ClientHello: el servidor rechazaba periódicamente conexiones de parte del pool de direcciones. Esos datos se pasaron al soporte, y allí confirmaron el cuadro con sus logs de la zona C.
Caso 2: cortes en descargas grandes exactamente cada 10 minutos
El cliente descargaba archivos de 300-800 MB a través de un proxy móvil y se quejaba de cortes a mitad. El dump mostró RST del proxy en momentos separados entre sí exactamente 600 segundos, con precisión de un segundo, independientemente del inicio de la descarga. La causa resultó ser una rotación de IP programada cada 10 minutos: al cambiar la dirección, el proxy cierra los túneles activos. La solución fue cambiar el puerto a rotación por petición y lanzar el cambio de dirección entre descargas. Los cortes desaparecieron por completo; hicieron falta dos horas de diagnóstico en lugar de semanas de correspondencia.
Caso 3: el servidor está caído y parece que la culpa es del proxy
Por la mañana todas las peticiones a una misma API empezaron a devolver error de conexión. Logs del cliente: Connection aborted. La primera reacción: el proxy se cayó. Un dump de un minuto mostró: el TCP con el proxy se establece en 38 ms, el CONNECT sale, y a los 30 segundos llega 504 y FIN. Al mismo tiempo, una petición a otro host por el mismo proxy pasaba en 300 ms. Diagnóstico: el servidor destino no acepta conexiones desde el proxy. Cuarenta minutos después, la página de estado del servicio destino confirmó el incidente de su lado. El dump ahorró la mañana y evitó un ticket falso al soporte del proxy.
Caso 4: las pérdidas de Wi-Fi parecían problema del proxy
Un desarrollador probaba una integración desde una laptop a través de un proxy móvil Proxeon y obtenía timeouts esporádicos. El dump mostró retransmisiones del cliente con una frecuencia del 6 por ciento y ACK duplicados del proxy, sin un solo RST del proxy. Conectar la laptop por cable redujo las retransmisiones a cero y los timeouts desaparecieron. El problema estaba en el punto de acceso sobrecargado de la oficina.
Lista de verificación para capturar un dump antes de contactar al soporte
Si vas a enviar un dump al soporte de un servicio de proxy, este es el orden de acciones que ahorrará tiempo a ambas partes.
Preparación
- Sincroniza el reloj de la máquina mediante NTP. Verifícalo con el comando timedatectl o date -u.
- Si es posible, crea credenciales de proxy de prueba para el diagnóstico o planifica cambiar la contraseña tras enviar el dump.
- Registra la versión del cliente, la librería HTTP, el SO y la forma de conexión a internet (cable, Wi-Fi, red celular).
- Anota la dirección y el puerto del proxy, el host destino, el comportamiento esperado y el real.
Captura
- Lanza tcpdump con filtro por host y puerto del proxy, con rotación, tamaño completo de paquete:
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'- Reproduce el problema. Si se reproduce con poca frecuencia, deja la captura el tiempo necesario; 48 archivos de 5 minutos cubrirán 4 horas.
- En paralelo a la captura, haz una petición de control con curl -v y --trace-time, guarda la salida. Dará una referencia exacta de tiempo con los paquetes.
- Registra la hora exacta (UTC) de cada manifestación del error en los logs del cliente.
- Detén tcpdump con Ctrl+C. Asegúrate de que los archivos no estén vacíos: capinfos proxy-*.pcap.
Procesamiento
- Une los archivos: mergecap -w all.pcapng proxy-*.pcap.
- Encuentra los flujos problemáticos con el filtro tcp.flags.reset == 1 o por la hora de los logs.
- Extrae solo los flujos necesarios: tshark -r all.pcapng -Y 'tcp.stream in {12 47 93}' -w report.pcapng. El soporte no necesita horas de tráfico normal.
- Verifica que en el dump recortado no haya nada de más: conexiones ajenas, tráfico de otros servicios.
- No adjuntes el archivo de claves TLS.
Texto de la comunicación
- Hora de manifestación en UTC con precisión de un segundo.
- Dirección y puerto del proxy, host destino.
- Interpretación breve: qué ves en el dump (por ejemplo, 200 al CONNECT, luego FIN del proxy 170 ms después del ClientHello, RTT hasta el proxy 45 ms).
- Números de trama o de flujo en el archivo adjunto.
- Salida de curl -v con marcas de tiempo.
- Qué ya verificaste y descartaste: otro host por el mismo proxy, el mismo host por otra conexión.
El soporte procesa una comunicación así de una sola pasada. Le basta con cotejar tus marcas de tiempo con los logs de la zona C y confirmar o refutar la versión.
FAQ
¿Por qué en el dump no aparece la dirección IP del servidor destino, si me estoy dirigiendo a él?
Porque no te diriges a él directamente, sino a través del proxy. Tu cliente establece una conexión TCP con la dirección del proxy y le pide que se conecte al host destino. La conexión proxy - servidor existe solo del lado del proxy. En el dump el nombre del host destino se ve en la línea CONNECT y en el SNI dentro del ClientHello, pero paquetes hacia su IP desde tu máquina no hay ni puede haber.
¿Se puede saber por el dump qué IP externa usó el proxy para dirigirse al servidor?
No. Esa información pertenece a la zona C. La única forma es solicitar a través del proxy un servicio que devuelva la dirección del cliente, o mirar en el panel del proveedor qué dirección estaba activa en ese momento.
El proxy envió un RST. ¿Entonces el problema está en el proxy?
No necesariamente. Un RST desde la dirección del proxy significa que el túnel se cerró del lado del proxy, y la causa puede estar en el servidor destino o en la red detrás del proxy. Mira el tiempo: si el RST llegó tras un tiempo notablemente mayor que el RTT hasta el proxy después de tu último paquete, lo más probable es que el proxy tradujera una reacción del servidor. Si el RST llegó en un momento fijo o tras un intervalo exacto, es probable una política del propio proxy, por ejemplo rotación o límite.
¿Cómo medir el RTT hasta el proxy por el dump?
La diferencia de tiempo entre el SYN tuyo y el SYN-ACK del proxy al inicio de cualquier conexión. En Wireshark puedes activar Statistics > TCP Stream Graphs > Round Trip Time para el flujo, o usar el campo tcp.analysis.ack_rtt como columna.
Wireshark no muestra TLS dentro del túnel, solo datos TCP. ¿Qué hacer?
Haz clic derecho en el paquete posterior a la respuesta 200 Connection established, elige Decode As, y en la columna Current elige TLS para el puerto TCP del proxy. Asegúrate también de que los paquetes no estén recortados (-s 0 al capturar) y de que el disector HTTP esté configurado en el puerto del proxy, si no es estándar: Edit > Preferences > Protocols > HTTP > TCP ports.
¿En qué se diferencia este enfoque de mitmproxy?
mitmproxy trabaja a nivel de aplicación: termina TLS con un certificado falso, lee y puede modificar peticiones HTTP. Para eso el cliente debe confiar en su certificado raíz. tcpdump y Wireshark trabajan a nivel de paquetes y no sustituyen nada: ves los paquetes reales, las banderas y los tiempos reales, incluidos errores TCP que mitmproxy ocultaría porque él mismo los manejaría. Para diagnosticar cortes, el nivel de paquetes es más honesto. Para ver el contenido de las peticiones es más simple mitmproxy, pero si quieres, el contenido de tu propio tráfico también se puede ver en Wireshark mediante SSLKEYLOGFILE sin sustituir certificados.
¿Se puede descifrar en Wireshark el tráfico que el proxy lleva con el servidor?
No. No tienes ni los paquetes de esa conexión ni las claves. SSLKEYLOGFILE da claves solo de las sesiones de tu cliente, y dentro del túnel CONNECT esa es justamente tu sesión con el servidor, por eso sí se puede descifrar. Pero no hay un TLS aparte entre el proxy y el servidor en el caso de CONNECT: el proxy simplemente retransmite tus bytes.
¿Cómo saber que el cliente se fuga por fuera del proxy?
Captura un dump sin filtro por el proxy, sino con filtro por toda tu interfaz, y mira las conexiones salientes a los puertos 80 y 443 hacia direcciones distintas de la del proxy, así como UDP 443 (QUIC) y consultas DNS a resolutores externos con nombres de los hosts destino. Filtro de Wireshark: (tcp.port == 443 || udp.port == 443) && !(ip.addr == 203.0.113.10). Cualquier coincidencia es una fuga de configuración.
¿Qué tan grande debe ser el dump para el soporte?
Cuanto más pequeño, mejor. Un solo flujo problemático recortado ocupa de 5 a 500 KB. Un archivo de más de 50 MB el soporte lo analizará más tiempo, y la mitad de ese volumen será tráfico normal. Recorta los flujos necesarios con tshark o con File > Export Specified Packets en Wireshark.
¿Qué hacer si tcpdump en una máquina virtual muestra paquetes de tamaño mayor que el MTU?
Es generic segmentation offload: el kernel entrega a tcpdump segmentos grandes antes de que la tarjeta de red los divida. No afecta al análisis de banderas, tiempos ni cortes. Si molesta, desactiva el offload durante la captura con el comando ethtool -K eth0 gso off tso off gro off, pero recuerda que eso reducirá el rendimiento de la red.
Conclusión
Un dump de tráfico al trabajar a través de un proxy no se trata de espiar el contenido ni de magia. Se trata de una grabación honesta de lo que realmente ocurría en el cable: con precisión de milisegundos y hasta la última bandera. Los logs del cliente dicen que la conexión se cortó. El dump dice quién lo hizo, cuándo exactamente y qué había antes.
Resumamos la metodología. En la máquina cliente solo ves la conexión hasta el proxy: el handshake TCP, el diálogo CONNECT o SOCKS y los registros TLS cifrados dentro del túnel. El servidor destino no se ve directamente, pero su comportamiento lo traduce el proxy a través de los códigos de respuesta al CONNECT, los tiempos y la forma de cerrar el túnel. Un RST o FIN desde tu dirección es tu problema o tu timeout. Retransmisiones y ACK duplicados sin cierre son pérdidas en el camino hasta el proxy. Un cierre desde el proxy tras un tiempo que supera varias veces el RTT hasta él es una reacción del servidor traducida. Un cierre en momentos fijos es política del proxy. Zero Window de tu lado es tu aplicación que no lee.
Pasos prácticos para hoy: guarda en marcadores los comandos de tcpdump con rotación y los filtros de Wireshark de este artículo; verifica si los relojes de las máquinas donde corren tus clientes están sincronizados; asegúrate de que tu cliente no tenga fuga de QUIC por fuera del proxy; crea credenciales de prueba para el diagnóstico, para no exponer las de producción en los dumps. Y la próxima vez que los logs digan connection reset by peer, no adivines. Captura un dump, abre el flujo, mira la dirección y el tiempo. En cinco minutos sabrás a quién escribirle: a ti mismo, al soporte del proxy o al dueño del servidor destino.