tcpdump et Wireshark à travers un proxy : capture de trafic et diagnostic des coupures
Sommaire de l'article
- Introduction : quand les logs du client ne suffisent plus
- Les bases : ce qu'on voit avant le proxy, dans le tunnel et après
- Analyse approfondie : pourquoi le proxy ne peut pas en montrer plus, et comment lire les indices indirects
- Tcpdump en pratique : filtres, écriture dans un fichier, rotation
- Wireshark : filtres d'affichage, follow tcp stream, lecture du tls sans déchiffrement
- Diagnostic à partir du dump : rst, fin, retransmission, zero window
- Déchiffrer son propre trafic via sslkeylogfile
- Tableaux typiques : timeout de connexion, coupure au milieu d'une réponse, pertes en réseau mobile
- Erreurs fréquentes lors de la capture et de la lecture d'un dump
- Outils et ressources
- Cas concrets et résultats
- Checklist pour capturer un dump destiné au support
- Faq
- Conclusion
Quand une application passe par un proxy et que quelque chose ne tourne pas rond, la première chose que l'ingénieur ouvre, ce sont les logs du client. On y lit des messages du genre connection reset by peer, read timeout ou simplement EOF. Le problème, c'est que ces lignes ne disent presque rien sur la cause réelle. Qui a coupé la connexion : le proxy, le serveur cible ou l'opérateur réseau entre vous et le proxy ? La requête a-t-elle seulement atteint le proxy ? Le handshake TLS a-t-il eu le temps de se terminer ? La bibliothèque HTTP du client ne le sait pas, et donc vous non plus.
Dans cet article, on descend d'un niveau, jusqu'aux paquets. On va voir comment capturer un dump tcpdump sur la machine cliente, ce qu'on y voit réellement quand on passe par un proxy HTTP ou SOCKS5, pourquoi dans un tunnel HTTPS on ne voit que le CONNECT et des enregistrements TLS chiffrés, comment lire le dump dans Wireshark et comment, grâce aux flags RST, FIN, aux retransmissions et à la fenêtre nulle, déterminer de quel côté la connexion s'est rompue. On parlera aussi du déchiffrement de son propre trafic via SSLKEYLOGFILE et de la bonne façon de préparer un dump pour contacter le support d'un service proxy, comme Proxeon.
Une mise au point importante : cet article n'est pas consacré à mitmproxy. Là-bas, il s'agit d'intercepter et de substituer HTTPS au niveau applicatif avec un certificat factice. Ici, on travaille au niveau paquet, on ne substitue rien et on ne perce pas le trafic des autres. Notre objectif est purement diagnostique : comprendre où se casse la chaîne client - proxy - serveur cible.
Introduction : quand les logs du client ne suffisent plus
Imaginez une situation classique. Un script Python passe par un proxy mobile, traite quelques milliers de requêtes par heure, et environ deux pour cent d'entre elles échouent avec Connection aborted, RemoteDisconnected. Le développeur ajoute des retries, l'erreur ne disparaît pas. Il écrit au support du proxy, qui demande un exemple de requête et l'heure. Le support consulte ses logs et répond : chez nous tout va bien, la connexion au serveur cible s'établissait. Qui a raison ?
Sans dump, ce débat est sans fin. Avec un dump, il se règle en cinq minutes : on voit que le proxy a répondu au CONNECT avec un code 200, que le client a envoyé un ClientHello, et que 180 millisecondes plus tard un RST est arrivé du proxy. Cela signifie soit que le proxy n'a pas réussi à s'entendre avec le serveur cible, soit que le serveur a lui-même fermé la connexion. Ensuite on peut regarder les timings et affiner. L'essentiel, c'est que la discussion passe du domaine des suppositions à celui des faits.
Les logs d'un client HTTP travaillent au niveau applicatif. Ils voient le résultat, pas le processus. Un dump de trafic montre le processus : chaque paquet avec un horodatage précis, une direction, des flags et une taille. C'est pour cela qu'en exploitation sérieuse d'une infrastructure proxy, savoir capturer et lire un dump fait partie des compétences de base, ce n'est pas de l'exotisme.
Ce que vous allez retenir de cet article
- Comprendre quelles données transitent physiquement par l'interface réseau du client lorsqu'on passe par un proxy, et ce qu'on peut en voir en clair.
- Des commandes tcpdump prêtes à l'emploi pour capturer un dump avec filtres, rotation et limitation de taille.
- Une série de filtres d'affichage Wireshark à copier directement.
- Une méthode pour distinguer une coupure côté client, côté proxy et côté serveur cible.
- Un moyen sûr de déchiffrer son propre trafic TLS pour le débogage.
- Une checklist pour préparer un dump destiné au support.
Les bases : ce qu'on voit avant le proxy, dans le tunnel et après
Commençons par le schéma. Quand on passe par un proxy, il y a trois segments de chemin, et depuis la machine cliente vous ne pouvez physiquement observer que le premier.
Trois zones d'observation
- Zone A : client - proxy. C'est la seule connexion TCP qui traverse votre interface réseau. tcpdump sur votre machine la voit. On y observe : le handshake TCP avec l'adresse IP du proxy, le protocole de service du proxy (HTTP CONNECT ou SOCKS5), puis soit du HTTP en clair, soit des enregistrements TLS chiffrés.
- Zone B : dans le tunnel. Une fois que le proxy a répondu 200 Connection established, tous les octets entre vous et le serveur cible sont simplement relayés. Si le serveur cible travaille en HTTPS, ces octets forment des enregistrements TLS. Vous en voyez la structure (type d'enregistrement, longueur, ClientHello avec SNI, ServerHello, alertes), mais pas le contenu.
- Zone C : proxy - serveur cible. C'est une connexion TCP distincte que le proxy établit en son nom depuis son adresse externe. Sur la machine cliente, elle n'existe pas. On ne peut la voir que sur le serveur proxy lui-même, et c'est l'infrastructure du fournisseur. Tout ce que vous apprenez sur la zone C, vous l'apprenez indirectement : par les codes de réponse au CONNECT, par les délais et par la manière dont le proxy ferme le tunnel.
C'est l'idée centrale de tout l'article. Un dump côté client ne montre pas directement le serveur cible. Mais il montre le comportement du proxy, et le proxy, en bon relais, transmet le comportement du serveur cible sous une forme interprétable. Notre travail consiste à apprendre à lire cette traduction.
Proxy HTTP et HTTP en clair
Le cas le plus limpide. Si le site cible fonctionne en http sans chiffrement, le client envoie au proxy une requête avec l'URI absolue :
GET http://example.com/api/items HTTP/1.1
Host: example.com
Proxy-Authorization: Basic dXNlcjpwYXNz
User-Agent: my-client/1.0Dans le dump on voit tout : la méthode, le chemin, les en-têtes, le corps, la réponse du proxy avec le corps du serveur. Remarquez l'en-tête Proxy-Authorization. Ce sont vos identifiants en base64, ils traînent en clair dans le dump. On garde ce fait en tête, il servira quand on parlera de la transmission d'un dump à des tiers.
Proxy HTTP et HTTPS via CONNECT
Là commence la partie intéressante. Pour HTTPS, le client demande d'abord au proxy d'établir un tunnel TCP jusqu'à l'hôte et au port :
CONNECT api.example.com:443 HTTP/1.1
Host: api.example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz
Proxy-Connection: Keep-AliveLe proxy répond :
HTTP/1.1 200 Connection establishedÀ partir de ce moment, le proxy cesse d'analyser le HTTP. Il copie simplement les octets d'un socket à l'autre. Le client commence le handshake TLS directement avec le serveur cible, et tout le trafic HTTP (GET, POST, en-têtes, corps) se retrouve à l'intérieur du TLS. Voilà pourquoi dans le dump d'un tunnel HTTPS on ne voit que le CONNECT : c'est la dernière chose que le client dit au proxy en clair. Ensuite arrivent les enregistrements TLS, que le proxy ne peut pas lire, et vous non plus dans le dump.
Ce qui reste visible à l'intérieur du tunnel :
- ClientHello : versions TLS, suites de chiffrement, extensions et, généralement, SNI (le nom d'hôte en clair).
- ServerHello : version et chiffrement choisis.
- En TLS 1.2, le certificat du serveur en clair. En TLS 1.3, le certificat est déjà chiffré.
- TLS Alert : le type d'alerte est visible en TLS 1.2 ; en TLS 1.3 les alertes après le handshake sont chiffrées, mais le simple fait d'un enregistrement de type Alert de 2 octets se repère.
- Les tailles et timings des enregistrements Application Data. On peut estimer combien de données sont arrivées avant la coupure.
SOCKS5
SOCKS5 fonctionne différemment, mais l'idée reste la même. Le client envoie une salutation avec les méthodes d'authentification (octets 05 02 00 02), le proxy choisit une méthode, puis vient l'authentification par nom et mot de passe (sous-protocole avec les octets 01, longueur, nom, longueur, mot de passe), puis la commande CONNECT avec le type d'adresse 03 (nom de domaine) et le port. Le proxy répond 05 00 en cas de succès ou un code d'erreur : 01 erreur générale, 03 réseau inaccessible, 04 hôte inaccessible, 05 connexion refusée, 06 TTL expiré. Ces codes d'erreur sont la traduction directe de ce que le proxy a vu en zone C. Wireshark sait décoder SOCKS, il suffit d'indiquer le port via Decode As.
Ce qu'on ne voit jamais
Depuis la machine cliente, vous ne verrez pas : l'IP externe du proxy depuis laquelle il contacte le serveur cible (pour un proxy mobile, c'est une adresse d'opérateur), la connexion TCP proxy - serveur, les requêtes DNS du proxy. Si le support Proxeon dit qu'il voit une erreur côté serveur cible, il regarde précisément dans la zone C, inaccessible pour vous. Votre travail consiste à apporter un dump de la zone A, pour recouper les deux tableaux par le temps.
Analyse approfondie : pourquoi le proxy ne peut pas en montrer plus, et comment lire les indices indirects
Il faut comprendre pourquoi le tableau est ainsi fait, et ce qu'on peut en tirer pour le diagnostic.
Le tunnel CONNECT comme un tuyau d'octets
Après la réponse 200, un proxy HTTP est tenu, par la spécification, de transmettre les octets dans les deux sens sans interprétation jusqu'à la fermeture par l'un des côtés. Il ne sait pas qu'il y a du TLS à l'intérieur. Il ne sait pas quelles requêtes HTTP vous envoyez. Il ne voit que deux événements : un flux d'octets et la fermeture du socket. Quand le serveur cible ferme la connexion avec le proxy (envoie un FIN ou un RST), le proxy doit fermer la connexion avec vous. La manière exacte dépend de l'implémentation : certains proxys transmettent un FIN comme un FIN et un RST comme un RST, d'autres transforment tout en FIN, d'autres encore envoient un RST au client en cas d'erreur de lecture sur le socket distant.
Conséquence pratique : un RST provenant de l'adresse IP du proxy ne signifie pas que le proxy est fautif. Cela signifie que le tunnel a été fermé du côté proxy, et la cause peut être n'importe où derrière lui. Pour trouver la cause, on regarde le contexte : ce qui s'est passé avant le RST, combien de temps s'est écoulé, si la réponse était arrivée.
Différence entre un SYN vers le proxy et un SYN vers le serveur cible
C'est la source de confusion la plus fréquente. Dans un dump via proxy, vous ne verrez jamais de SYN vers le port 443 du serveur cible. Tous les SYN partent vers l'IP et le port du proxy. Si le SYN vers le proxy ne reçoit pas de SYN-ACK et se répète à intervalles de 1, 2, 4, 8 secondes, le problème est entre vous et le proxy : réseau, pare-feu, mauvaise adresse ou mauvais port, proxy arrêté. Le serveur cible n'est pas concerné, vous n'avez même pas tenté de l'atteindre, en tout cas pas dans les termes de votre dump.
En revanche, si le TCP avec le proxy est établi, si le CONNECT est parti et que la réponse arrive au bout de 20-30 secondes avec un code 504 Gateway Timeout ou 502 Bad Gateway, c'est que le proxy n'a pas réussi à établir la zone C. Le timing parle de lui-même : votre paquet CONNECT est parti instantanément, la réponse a mis longtemps, le proxy attendait donc un timeout dans sa propre tentative.
TLS 1.3, ECH et ce qui change à l'horizon 2026
Le paysage se referme lentement. TLS 1.3 domine, et là le certificat du serveur est chiffré : sans clés, impossible de vérifier dans le dump quel certificat le serveur a renvoyé. L'extension ECH (Encrypted Client Hello) se déploie peu à peu dans les navigateurs et les grands CDN, et le SNI devient lui aussi chiffré. Pour le diagnostic via proxy, c'est moins critique, car le nom d'hôte reste visible dans la ligne CONNECT, mais le filtre habituel sur le SNI s'activera de moins en moins souvent.
Autre tendance : HTTP/3 sur QUIC. Un proxy HTTP classique ne tunnelise que du TCP via CONNECT. Si dans votre dump vous voyez soudain du trafic UDP vers le port 443 directement à l'adresse du serveur cible, en contournant le proxy, c'est le signe d'une fuite : l'application tente d'utiliser QUIC en direct au lieu de passer par le proxy. Un client bien configuré doit soit désactiver QUIC lorsqu'il passe par un proxy, soit utiliser des mécanismes spécifiques de proxy UDP. Filtre pour vérifier rapidement : udp.port == 443. Si le dump via proxy montre ce type de paquets, il faut corriger la configuration du client.
Où placer le point de capture
La réponse est souvent simple : sur la machine cliente. Mais il y a des nuances. Si le client tourne dans un conteneur Docker, tcpdump sur l'hôte, sur l'interface docker0 ou sur l'interface du bridge, montrera le trafic avant NAT, tandis que sur l'interface externe, ce sera après NAT, avec une autre adresse source. Le plus simple est d'entrer dans l'espace réseau du conteneur : nsenter -t PID -n tcpdump ... ou de lancer un conteneur avec tcpdump via --net=container:nom. Sur les machines virtuelles dans le cloud, attention à l'offloading : les paquets du dump peuvent paraître plus gros que le MTU, parce que la carte réseau agrège les segments. C'est normal et ça n'affecte pas l'analyse des flags.
tcpdump en pratique : filtres, écriture dans un fichier, rotation
tcpdump est présent sur presque toutes les machines Linux et macOS. Sous Windows, c'est Wireshark avec le pilote Npcap, ou l'outil en ligne de commande dumpcap, qui joue le même rôle. Voyons les commandes qui couvrent 95 pour cent des besoins de diagnostic proxy.
Capture de base vers le proxy
Supposons que l'adresse du proxy soit 203.0.113.10, port 8080. On enregistre tout ce qui circule entre nous et le proxy dans un fichier :
sudo tcpdump -i any -nn -s 0 -w proxy.pcap 'host 203.0.113.10 and port 8080'Décryptage des options :
- -i any : écouter sur toutes les interfaces. Pratique quand on ne sait pas laquelle est utilisée. Si on la connaît, mieux vaut préciser : -i eth0 ou -i wlan0. Sous macOS, -i any n'est pas supporté, indiquez en0.
- -nn : ne pas résoudre les IP en noms ni les ports en noms de services. La résolution ralentit la capture et ajoute des requêtes DNS parasites.
- -s 0 : capturer le paquet en entier. Dans les versions modernes c'est la valeur par défaut, mais l'indiquer explicitement ne fait pas de mal.
- -w proxy.pcap : écrire dans un fichier au format pcap plutôt qu'à l'écran. C'est la seule façon d'ouvrir ensuite le dump dans Wireshark.
- Filtre entre guillemets simples : c'est un filtre de capture BPF. Il élimine tout le superflu directement dans le noyau, donc le fichier ne contiendra que l'essentiel.
Filtres par hôte et par port
Plusieurs proxys ou un pool de ports :
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and portrange 10000-10100'Proxy par nom de domaine (tcpdump le résoudra une seule fois au démarrage, ce qui ne convient pas toujours pour des adresses rotatives) :
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host proxy.example.net and port 8080'Capture des seuls paquets de signalisation sans données, pour observer les handshakes et les coupures sur de gros volumes :
sudo tcpdump -i eth0 -nn -w flags.pcap 'host 203.0.113.10 and tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'Uniquement les RST dans les deux sens :
sudo tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'Capture en excluant sa propre session SSH, pour ne pas polluer le dump quand on capture sur un serveur distant :
sudo tcpdump -i eth0 -nn -w proxy.pcap 'host 203.0.113.10 and not port 22'Comment éviter les gigaoctets : rotation par taille et par temps
Si l'erreur est rare et se reproduit une fois par heure, le dump doit tourner longtemps. Sans rotation, le disque sera plein. Rotation par taille, 100 Mo par fichier, anneau de 10 fichiers :
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'Ici -C fixe la taille en millions d'octets, -W limite le nombre de fichiers : le onzième écrasera le premier. Rotation par temps, un nouveau fichier toutes les 10 minutes, en gardant les 24 derniers (4 heures) :
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'L'option -G spécifie l'intervalle en secondes, et le motif de temps dans le nom de fichier est obligatoire, sinon tcpdump réécrira le même fichier. L'option -Z utilisateur abandonne les privilèges après l'ouverture de l'interface, et -U force l'écriture immédiate des paquets sur disque sans mise en tampon, ce qui est utile si vous lisez le fichier en parallèle ou si vous craignez de perdre la fin en cas d'arrêt brutal.
Tronquer la charge utile
Autre moyen de réduire le volume : ne capturer que les en-têtes. Pour diagnostiquer des coupures, le contenu des enregistrements TLS n'est pas nécessaire, les 128 premiers octets de chaque paquet suffisent :
sudo tcpdump -i eth0 -nn -s 128 -w headers.pcap 'host 203.0.113.10'Attention : avec -s 128, la ligne CONNECT et les en-têtes peuvent être tronqués, et Wireshark marquera les paquets comme truncated. Pour analyser entièrement le handshake TLS, c'est insuffisant, un ClientHello fait souvent 300 à 600 octets et plus. Un compromis : -s 600.
Lire un dump directement dans la console
Wireshark n'est pas toujours à portée de main, mais on veut jeter un œil vite. Lecture du fichier avec affichage des flags TCP et des numéros de séquence absolus :
tcpdump -nn -r proxy.pcap -S -tttt 'tcp[tcpflags] & (tcp-rst|tcp-fin) != 0'L'option -tttt affiche la date et l'heure complètes, -S montre les numéros de séquence absolus, ce qui aide à recouper avec les logs. Affichage du contenu des paquets en ASCII, pour voir la ligne CONNECT et la réponse du proxy :
tcpdump -nn -r proxy.pcap -A 'port 8080' | grep -E 'CONNECT|HTTP/1'tshark comme Wireshark en console
Si Wireshark est installé sur le serveur, tshark donne accès aux mêmes dissecteurs depuis la console. Liste de tous les CONNECT avec les codes de réponse :
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.codeListe des flux terminés par un RST, avec indication de l'émetteur :
tshark -r proxy.pcap -Y 'tcp.flags.reset == 1' -T fields -e frame.time_relative -e tcp.stream -e ip.src -e ip.dstWireshark : filtres d'affichage, Follow TCP Stream, lecture du TLS sans déchiffrement
Vous avez ouvert le pcap dans Wireshark et vous voyez des milliers de lignes. Par où commencer ? Par les filtres d'affichage. Contrairement aux filtres de capture BPF, ils s'appliquent au fichier déjà enregistré et comprennent la structure des protocoles.
Filtres de base pour le travail via proxy
Tout ce qui touche au proxy :
ip.addr == 203.0.113.10 && tcp.port == 8080Toutes les requêtes CONNECT :
http.request.method == "CONNECT"CONNECT vers un hôte précis :
http.request.method == "CONNECT" && http.host contains "api.example.com"Réponses du proxy autres que 200 (erreurs d'authentification 407, indisponibilité 502, timeouts 504) :
http.response.code >= 400 && tcp.port == 8080Uniquement les réponses 407, signe d'identifiants invalides ou de quota épuisé :
http.response.code == 407Filtres TLS à l'intérieur du tunnel
Wireshark sait reconnaître qu'après un CONNECT avec réponse 200 commence du TLS, et applique automatiquement le dissecteur TLS. S'il ne le fait pas (ça arrive avec des paquets tronqués), faites un clic droit sur le paquet, choisissez Decode As et indiquez TLS pour ce port.
Tous les ClientHello :
tls.handshake.type == 1ClientHello avec un SNI précis :
tls.handshake.extensions_server_name contains "example.com"ServerHello (s'il n'y en a pas après le ClientHello, le handshake n'a pas démarré côté serveur) :
tls.handshake.type == 2Alertes TLS :
tls.alert_messageTrouver un flux qui contient un ClientHello mais pas de ServerHello est plus délicat avec un seul filtre ; passez plutôt par Statistics > Conversations, triez par nombre de paquets et regardez les flux à 5-7 paquets.
Filtres sur les problèmes TCP
Tous les paquets avec RST :
tcp.flags.reset == 1Les RST envoyés par le proxy vers nous :
tcp.flags.reset == 1 && ip.src == 203.0.113.10Les RST que nous avons envoyés :
tcp.flags.reset == 1 && ip.dst == 203.0.113.10Les retransmissions :
tcp.analysis.retransmissionFenêtre nulle et ses conséquences :
tcp.analysis.zero_window || tcp.analysis.window_fullToutes les anomalies que Wireshark a repérées lui-même (retransmissions, ACK dupliqués, segments perdus, fenêtre nulle) :
tcp.analysis.flags && !tcp.analysis.window_updateSYN sans réponse, c'est-à-dire des SYN répétés :
tcp.flags.syn == 1 && tcp.flags.ack == 0 && tcp.analysis.retransmissionGrandes pauses dans un flux, plus de 5 secondes entre paquets :
tcp.time_delta > 5Pour ce filtre, il faut activer Edit > Preferences > Protocols > TCP > Calculate conversation timestamps.
Follow TCP Stream
Vous avez trouvé un paquet suspect, par exemple un RST. Clic droit > Follow > TCP Stream. Wireshark affiche tout le dialogue de cette connexion sous forme de texte : notre trafic d'une couleur, celui du proxy d'une autre. Pour du HTTPS via CONNECT, vous verrez la ligne CONNECT, la réponse 200 Connection established, puis des octets TLS illisibles. C'est normal. Ce qu'il faut regarder, ce sont les volumes : combien d'octets sont partis de chez nous (notre ClientHello et nos requêtes), combien sont arrivés (ServerHello et réponse) et où tout s'est arrêté.
En bas de la fenêtre Follow, il y a des compteurs : tant d'octets client, tant d'octets serveur. Si 0 octet est arrivé du serveur après la réponse 200, le serveur cible n'a pas répondu du tout au ClientHello. Si 100 à 4000 octets sont arrivés puis coupure, le handshake a commencé mais ne s'est pas terminé. Si des dizaines de kilooctets sont arrivés puis coupure, le problème est au milieu du transfert de données.
Une bonne habitude : filtrer par numéro de flux. Après un Follow, la ligne de filtre se remplit automatiquement d'un tcp.stream eq 42. Fermez la fenêtre Follow, et la liste principale ne montrera plus que ce flux avec ses flags et ses timings. C'est la vue la plus pratique pour analyser une coupure.
Lire un handshake TLS sans déchiffrement
Dépliez le ClientHello dans l'arbre du paquet. Ce qu'il faut regarder :
- Version et supported_versions : le client propose-t-il TLS 1.3 ? Si le serveur exige la 1.3 et que le client ne propose que la 1.2, le serveur fermera la connexion avec une alerte protocol_version ou un simple FIN.
- server_name : le SNI correspond-il à l'hôte du CONNECT ? Une divergence se produit avec une mauvaise configuration client et provoque des erreurs de certificat.
- Cipher Suites : la liste des chiffrements. Une liste trop courte ou obsolète est une cause d'alerte handshake_failure.
- ALPN : le client propose-t-il h2 ? Si le serveur choisit h2 alors que le client attendait du HTTP/1.1, l'application peut planter avec des erreurs incompréhensibles.
Dans le ServerHello, regardez la version et le chiffrement choisis. En TLS 1.2, le Certificate qui suit est en clair, on peut vérifier le nom et la date d'expiration. En TLS 1.3, presque tout est chiffré après le ServerHello, et ce qu'on lit ensuite, c'est le type d'enregistrement. Application Data signifie que le handshake s'est terminé avec succès et que les données commencent. Une alerte de 2 octets juste après le ServerHello, ou à sa place, signifie que quelque chose ne va pas : en TLS 1.3 sans clés vous ne verrez pas le code de l'alerte, mais le fait même est informatif.
Dans Statistics > Conversations > TCP, on évalue bien la vue d'ensemble : combien de flux, combien d'octets dans chaque sens, quelle durée. Les flux de 0,05 seconde et 6 paquets sont probablement des connexions réinitialisées juste après le handshake.
Diagnostic à partir du dump : RST, FIN, retransmission, zero window
Maintenant le cœur du sujet. Comment, à partir d'un dump de la zone A, savoir où exactement la connexion s'est rompue ? Passons en revue chaque signal et son interprétation dans le contexte proxy.
La matrice des directions
La première question est toujours la même : qui a envoyé le paquet de fermeture ? Dans le dump, c'est le champ ip.src. Il n'y a que deux possibilités : notre adresse ou celle du proxy.
- RST ou FIN de notre côté : c'est notre application, notre OS ou quelque chose sur notre hôte qui a fermé la connexion. Le proxy et le serveur cible n'y sont pour rien. Causes typiques : timeout du client HTTP, arrêt brutal du processus, épuisement des descripteurs de fichiers, action d'un pare-feu ou d'un antivirus local.
- RST ou FIN du côté du proxy : le tunnel a été fermé côté proxy. La cause est soit dans le proxy lui-même (limites, politique, timeout d'inactivité), soit transmise depuis le serveur cible, soit venue du réseau entre le proxy et le serveur. On les distingue par le contexte et les timings.
- Ni RST ni FIN, seulement des retransmissions : les paquets se perdent sur le chemin entre nous et le proxy. Aucun des deux côtés n'a fermé la connexion, elle est simplement morte des pertes.
RST après CONNECT sans réponse 200
Le tableau : TCP établi, on envoie le CONNECT, le proxy répond RST sans réponse HTTP. Ce comportement signifie généralement que le proxy a refusé au niveau de la politique ou n'a pas pu décoder la requête. Causes : port de destination non autorisé, limite de connexions simultanées, format de requête incorrect. Ici le problème est en zone A ou dans le proxy lui-même. Le serveur cible n'y est pour rien.
Réponses 502, 503 ou 504 au CONNECT
Le tableau : le CONNECT part, N secondes plus tard arrive une réponse HTTP avec un code 5xx. Regardez N. Si la réponse arrive dans un temps de l'ordre du RTT jusqu'au proxy, le proxy a refusé immédiatement : peut-être un nom DNS qui ne résout pas de son côté ou une adresse immédiatement inaccessible (ICMP unreachable). Si N avoisine 10-30 secondes, le proxy a attendu un timeout en se connectant au serveur cible : le serveur ne répond pas au SYN. Dans les deux cas, c'est la zone C, et votre dump prouve que vous avez tout fait correctement, le proxy a honnêtement signalé son impossibilité.
FIN ou RST juste après le ClientHello
Le tableau : CONNECT - 200 - notre ClientHello - puis, après environ un RTT jusqu'au proxy plus quelque chose, un FIN ou un RST arrive du proxy. Deux candidats : le serveur cible a rejeté la connexion au stade TLS (SNI qui ne lui plaît pas, version, absence de certificat client) ou un système de protection du serveur a réinitialisé la connexion sur la base du ClientHello. L'indice clé, c'est le temps. Si entre notre ClientHello et la coupure il s'est écoulé un délai nettement supérieur au RTT jusqu'au proxy, c'est que le proxy a eu le temps de transmettre le ClientHello et de recevoir une réaction. Ce n'est pas le proxy qui a décidé de fermer, c'est la réaction du serveur cible qui a été transmise.
Comparez avec le RTT jusqu'au proxy, que vous mesurez sur le handshake TCP : le temps entre SYN et SYN-ACK. Si le RTT jusqu'au proxy est de 40 ms et que la coupure après le ClientHello arrive au bout de 200 ms, la différence de 160 ms correspond à peu près au RTT proxy - serveur aller-retour. Le tableau est parfaitement cohérent avec un refus du serveur.
Coupure après le ServerHello ou après une partie des données
Le handshake a commencé, le serveur a répondu, les données ont circulé, et au milieu arrive un FIN ou un RST. S'il s'agit d'un FIN venant du proxy et que l'enregistrement Application Data précédent a une taille logique, le serveur a peut-être simplement fermé après la réponse (Connection: close à l'intérieur du TLS), et le client l'a mal interprété. S'il s'agit d'un RST au milieu du flux de données sans ralentissement préalable, c'est soit une fermeture forcée côté serveur, soit le proxy qui a interrompu le tunnel sur un quota de trafic ou une durée de vie de session. Pour un proxy mobile avec rotation d'IP dans le temps, la seconde hypothèse est très probable : la coupure survient exactement au moment du changement d'IP. Vérifiez si l'heure de la coupure coïncide avec l'intervalle de rotation configuré sur votre proxy.
Retransmissions et leur direction
Wireshark marque un paquet comme retransmission quand il voit une nouvelle transmission des mêmes numéros de séquence. Regardons qui retransmet :
- C'est nous qui retransmettons : nos paquets ne sont pas acquittés par le proxy. Soit ils se perdent sur le chemin vers le proxy, soit les acquittements se perdent au retour. Dans les deux cas, le problème est dans le réseau de la zone A.
- C'est le proxy qui retransmet : ses paquets ne sont pas acquittés par nous. Nous ne les recevons pas ou nos ACK n'arrivent pas. Encore la zone A, mais plutôt sur le sens entrant.
- Retransmissions de SYN : cas à part, le proxy est injoignable au niveau TCP, la connexion ne s'établit pas du tout.
Important : les pertes en zone C ne se verront jamais sous forme de retransmissions. Le proxy gère seul le serveur cible. La seule trace des pertes en zone C, ce sont des pauses : le proxy nous transmet les données de manière irrégulière, avec des trous, alors qu'il n'y a aucune retransmission dans le dump. Le filtre tcp.time_delta > 1 pour les paquets venant du proxy révélera ces pauses.
Zero Window
Une fenêtre TCP de taille nulle signifie que le destinataire n'arrive pas à vider le tampon du socket assez vite. Si c'est nous qui annonçons une fenêtre nulle, notre application ne lit pas la réponse assez rapidement : thread occupé, verrou, traitement lent. Le proxy attend alors, envoie un Zero Window Probe, et si l'application ne se met toujours pas à lire, il peut fermer la connexion au bout de quelques dizaines de secondes. Le client verra une coupure et accusera le proxy, alors que la cause est chez lui.
Si c'est le proxy qui annonce une fenêtre nulle, c'est qu'il n'arrive pas à transmettre nos données plus loin : le serveur cible accepte lentement. C'est un indice indirect de problèmes en zone C, qu'on rencontre lors du téléchargement de gros fichiers via un proxy vers un serveur lent.
ACK dupliqués et SACK
Des ACK dupliqués venant du proxy signalent qu'il a reçu un paquet dans le désordre, quelque chose de notre côté s'est perdu. Beaucoup d'ACK dupliqués suivis de retransmissions, c'est le tableau classique de pertes dans un réseau mobile ou Wi-Fi de notre côté. Des ACK dupliqués venant de nous, c'est des paquets du proxy qui se sont perdus. La présence de l'option SACK dans le handshake permet à TCP de récupérer plus efficacement, mais le fait des pertes reste visible.
Heuristique du TTL
Une astuce avancée. Regardez le champ IP TTL dans les paquets normaux venant du proxy et dans le paquet RST. Si le TTL du RST diffère de plusieurs unités, le RST n'a pas été généré par le proxy lui-même, mais par un nœud intermédiaire sur le chemin : pare-feu, load balancer ou système de filtrage de l'opérateur. Ce n'est pas une preuve absolue, mais c'est un fort indice qu'il faut examiner le chemin entre vous et le proxy plutôt que d'accuser le proxy ou le serveur cible.
Tableau récapitulatif des interprétations
- SYN répété, pas de SYN-ACK : proxy injoignable ou réseau jusqu'à lui. Zone A.
- SYN - RST : port du proxy fermé ou filtré. Zone A.
- CONNECT - 407 : authentification erronée ou quota du compte. Proxy.
- CONNECT - 502/504 rapide : le proxy n'a pas pu démarrer la connexion au serveur. Zone C, probablement DNS ou routage.
- CONNECT - 504 après 20-30 secondes : le serveur ne répond pas au SYN du proxy. Zone C.
- 200 - ClientHello - RST/FIN après un temps supérieur au RTT : le serveur a rejeté le TLS. Zone C.
- 200 - ClientHello - silence - coupure après timeout du client : le serveur accepte le TCP mais ne répond pas au TLS. Zone C ou filtre bloquant derrière le proxy.
- Les données circulent - RST du proxy à un moment fixe : limite ou rotation du proxy. Proxy.
- Retransmissions et ACK dupliqués sans RST : pertes sur le réseau entre nous et le proxy. Zone A.
- Zero Window de notre côté : notre application ne lit pas. Client.
- RST de notre côté après un long silence : notre timeout. Client.
Déchiffrer son propre trafic via SSLKEYLOGFILE
Parfois les flags ne suffisent pas et il faut voir ce que le serveur a réellement renvoyé à l'intérieur du TLS : code de réponse, en-têtes, corps de l'erreur. Pour cela, pas besoin de mitmproxy ni de certificat factice. Il suffit que votre propre client écrive les clés de session dans un fichier, et que Wireshark les utilise pour déchiffrer. Cela ne fonctionne que pour votre client et vos connexions : les clés n'existent que chez la partie qui a participé au handshake. Déchiffrer le trafic d'un autre, ou celui que le proxy échange avec le serveur en zone C, est impossible par ce moyen, et c'est tant mieux.
Comment l'activer dans différents clients
Les navigateurs basés sur Chromium et Firefox lisent la variable d'environnement SSLKEYLOGFILE et y écrivent les clés au format NSS Key Log :
export SSLKEYLOGFILE=/home/user/tls-keys.log
google-chrome --proxy-server=http://203.0.113.10:8080curl, compilé avec OpenSSL, lit aussi cette 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 avec l'option de ligne de commande :
node --tls-keylog=/home/user/tls-keys.log app.jsPython ne lit pas la variable automatiquement, mais depuis la version 3.8, l'objet SSLContext possède l'attribut keylog_filename. Pour requests via un adaptateur :
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 le champ KeyLogWriter de 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,
}Brancher les clés dans Wireshark
Deux méthodes. La première : Edit > Preferences > Protocols > TLS > (Pre)-Master-Secret log filename, indiquer le chemin du fichier de clés. Wireshark déchiffrera tous les flux pour lesquels il trouve une correspondance par Client Random. La seconde est plus fiable pour transmettre le fichier à des collègues : intégrer les clés directement dans le pcapng :
editcap --inject-secrets tls,/home/user/tls-keys.log proxy.pcap proxy-with-keys.pcapngAprès cela, dans Wireshark, à l'intérieur du tunnel CONNECT apparaîtront les requêtes et réponses HTTP/1.1 ou HTTP/2 déchiffrées. Filtre pour le HTTP/2 déchiffré :
http2.header.name == ":status"Pour du HTTP/1.1 déchiffré à l'intérieur du tunnel, les filtres habituels http.response.code et http.request.uri fonctionnent.
Ce que cela apporte au diagnostic via proxy
Le déchiffrement lève la dernière incertitude. Vous voyez que le serveur a répondu 429 avec un en-tête Retry-After puis a fermé la connexion, ou qu'il a renvoyé 200 mais que le corps s'est interrompu à 40 pour cent, ou que la requête est partie et qu'il n'y a jamais eu de réponse. Avec un tel tableau, contacter le support du proxy devient concret : soit le problème est clairement côté serveur, soit clairement dans le tunnel.
Règles de sécurité
- Le fichier de clés permet de déchiffrer entièrement les sessions enregistrées, y compris les cookies et les jetons. Gardez-le comme un mot de passe et supprimez-le après analyse.
- Ne transmettez pas le fichier de clés avec le dump au support du proxy ni à qui que ce soit d'autre. Pour analyser des coupures, le support n'a pas besoin des clés, les flags et les timings suffisent.
- Si vous devez malgré tout montrer un contenu déchiffré, faites-le avec un compte de test sur le service cible et des identifiants proxy de test.
- Ne laissez pas SSLKEYLOGFILE activé en production. Une variable d'environnement qui atterrit par accident dans la config d'un service écrira des clés sur disque pendant des années.
Tableaux typiques : timeout de connexion, coupure au milieu d'une réponse, pertes en réseau mobile
Rassemblons les indices déjà vus en scénarios reconnaissables. Chacun est décrit tel qu'il apparaît dans la liste des paquets Wireshark après le filtre tcp.stream eq N.
Tableau 1 : timeout de connexion au 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)Aucun paquet du proxy. Les intervalles exponentiels 1, 2, 4, 8 secondes sont le backoff standard de retransmission du noyau Linux. Diagnostic : le proxy est injoignable depuis votre point. Vérifiez l'adresse, le port, le pare-feu, le routage, et aussi que l'adresse du proxy n'a pas changé. S'il s'agit d'un proxy mobile Proxeon avec un port dédié, assurez-vous que le port de votre espace client correspond bien à celui de la configuration du client.
Tableau 2 : timeout de connexion du proxy vers le serveur cible
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 jusqu'au proxy de 41 ms, le proxy a acquitté la réception du CONNECT, puis est resté silencieux pendant 30 secondes et a renvoyé un 504. Diagnostic : le serveur cible ne répond pas à la tentative de connexion du proxy. Causes possibles : serveur en panne, port fermé pour le pool d'adresses du proxy, problèmes réseau sur la route proxy - serveur. Votre client et votre réseau n'y sont pour rien.
Tableau 3 : le serveur rejette le 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]Coupure 164 ms après le ClientHello, avec un RTT jusqu'au proxy de 45 ms. La différence d'environ 120 ms correspond au temps mis par le proxy pour relayer le ClientHello au serveur et recevoir une fermeture. Diagnostic : le serveur a accepté le TCP mais a fermé la connexion au stade TLS. Vérifiez les paramètres du handshake : versions, chiffrements, SNI, ALPN. Si le ServerHello est absent et qu'il n'y a pas d'alerte, le serveur a fermé silencieusement, ce que font souvent les systèmes de protection face à un ClientHello atypique.
Tableau 4 : coupure au milieu d'une réponse
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]Le handshake s'est déroulé, environ 500 Ko de données sont arrivés, puis un RST sans ralentissement, sans retransmission, sans zero window. Regardez le temps absolu. S'il coïncide avec le moment de la rotation d'IP sur le proxy mobile ou avec l'expiration d'une limite de durée de session, la cause est dans le proxy, et la solution est d'aligner l'intervalle de rotation sur la durée de vos requêtes ou d'utiliser un mode sans rotation pendant les longs téléchargements. S'il n'y a pas de coïncidence, une coupure côté serveur ou CDN est probable. Ici le déchiffrement via SSLKEYLOGFILE aide : si à l'intérieur on voit un en-tête Content-Length et que le corps n'est pas arrivé en entier, le serveur a interrompu la transmission.
Tableau 5 : pertes dans le réseau mobile côté client
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)Des ACK dupliqués venant du proxy, suivis de retransmissions de notre côté avec des intervalles croissants. Ni RST ni FIN. Diagnostic : pertes sur le chemin client - proxy. Si le client lui-même est connecté via un réseau cellulaire ou Wi-Fi, c'est attendu lors des bascules entre stations de base. Solution : augmenter les timeouts du client, activer TCP keepalive, éviter les connexions longues et inactives, car les NAT des opérateurs suppriment les entrées des connexions idle, généralement au bout de 30 à 300 secondes, et le paquet suivant part dans le vide. Filtre pour évaluer l'ampleur des pertes :
tcp.analysis.retransmission && ip.dst == 203.0.113.10Comptez le pourcentage de ces paquets par rapport au total via Statistics > Capture File Properties. Un ou deux pour cent, c'est tolérable en réseau mobile ; au-delà de cinq, cherchez le problème dans les conditions radio ou dans l'équipement.
Tableau 6 : notre propre 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 requête est partie, le proxy a acquitté, aucune réponse n'est arrivée, et exactement 10 secondes plus tard, c'est nous qui envoyons le FIN. C'est notre read timeout. L'erreur dans le log client ressemblera à une coupure, mais le dump montre clairement : c'est nous qui avons fermé la connexion, parce que le serveur réfléchissait plus longtemps que nous n'étions prêts à attendre. Solution : soit augmenter le timeout, soit comprendre pourquoi le serveur est lent, mais le proxy ici a simplement attendu honnêtement avec nous.
Erreurs fréquentes lors de la capture et de la lecture d'un dump
Listons ce sur quoi même des ingénieurs expérimentés trébuchent régulièrement.
- Capturer sans filtre de capture sur une machine chargée. En une minute on accumule un gigaoctet, et retrouver les 200 paquets utiles devient pénible. Filtrez toujours par hôte du proxy.
- Filtrer par l'adresse du serveur cible. Via un proxy, ces paquets n'existent pas sur votre machine. Le filtre renvoie du vide, et on en conclut que le trafic ne passe pas. Il passe, mais vers le proxy.
- Confondre filtre de capture et filtre d'affichage. La syntaxe BPF de tcpdump (host, port, tcp[tcpflags]) et celle de Wireshark (ip.addr, tcp.port, tcp.flags.reset) sont différentes. Un filtre Wireshark dans tcpdump provoque une erreur de syntaxe, et inversement.
- Voir un RST du proxy et accuser immédiatement le proxy. Un RST provenant de l'adresse du proxy, c'est la fermeture du tunnel, pas un aveu de culpabilité. Regardez les timings et ce qui a précédé le RST.
- Ignorer le RTT. Sans délai mesuré jusqu'au proxy, impossible de distinguer un refus instantané du proxy d'un refus du serveur relayé.
- Capturer avec -s 64 puis essayer de lire le CONNECT. Les en-têtes seront tronqués. Pour le diagnostic proxy, une capture complète est nécessaire, ou au minimum -s 600.
- Ne pas synchroniser l'heure. Si l'horloge de la machine qui a capturé retarde d'une minute, recouper le dump avec les logs du support sera impossible. Activez NTP et indiquez l'heure UTC dans votre demande.
- Envoyer un dump avec les identifiants proxy. L'en-tête Proxy-Authorization dans une requête HTTP en clair vers le proxy contient le login et le mot de passe. Soit vous capturez avec des données de test, soit vous changez le mot de passe après l'envoi.
- Envoyer le fichier de clés avec le dump. Cela révèle tout le contenu des sessions. Le support n'a pas besoin des clés pour analyser des coupures.
- Garder un seul dump long sans rotation. Le disque se remplira au pire moment, et tcpdump tombera avec les paquets utiles.
- Ne pas vérifier la fuite QUIC. De l'UDP sur 443 en direct est le signe qu'une partie du trafic contourne le proxy, et le diagnostic par tunnel TCP ne montrera rien.
- Oublier le segmentation offload. Des paquets de 60 Ko dans un dump sur machine virtuelle ne signifient pas un MTU incorrect. C'est le noyau qui donne à tcpdump des segments non encore découpés.
Outils et ressources
L'ensemble minimal pour appliquer la méthode décrite.
Capture
- tcpdump : le standard sous Linux et macOS. Installé presque partout, nécessite root ou la capability CAP_NET_RAW.
- dumpcap : le captureur en ligne de commande fourni avec Wireshark, il prend en charge les mêmes options de rotation (-b filesize, -b files) et le format pcapng.
- Wireshark avec Npcap : pour Windows. On peut capturer directement depuis l'interface graphique avec un filtre de capture dans la même syntaxe BPF.
- tshark : analyse en console avec les dissecteurs de Wireshark, pratique sur les serveurs sans interface graphique et pour l'automatisation.
Analyse
- Wireshark : l'outil principal. Follow TCP Stream, Expert Info, Statistics > Conversations, IO Graph pour visualiser les pauses et les pics de retransmissions.
- editcap : découpage et filtrage de pcap, injection de clés TLS dans du pcapng.
- mergecap : fusion des fichiers de rotation en un seul pour analyse.
- capinfos : résumé rapide d'un fichier : durée, nombre de paquets, taille.
Clients compatibles débogage
- curl avec les options -v et --trace-time reproduit le tableau du dump au niveau applicatif et prend en charge SSLKEYLOGFILE.
- openssl s_client -proxy host:port permet d'exécuter manuellement le CONNECT et le handshake TLS via le proxy et de voir la réponse du serveur sans client HTTP.
One-liners utiles
Fusionner les fichiers de rotation :
mergecap -w all.pcapng proxy-*.pcapExtraire d'un dump un seul flux pour l'envoyer au support :
tshark -r all.pcapng -Y 'tcp.stream eq 42' -w stream42.pcapngStatistiques rapides sur les flux avec RST :
tshark -r all.pcapng -q -z conv,tcp | head -40Vérification manuelle d'un CONNECT via openssl :
openssl s_client -proxy 203.0.113.10:8080 -connect api.example.com:443 -servername api.example.com -briefCas concrets et résultats
Cas 1 : deux pour cent de coupures, le client était fautif
Un service de collecte de prix tournait via un pool de proxies mobiles, environ 40 000 requêtes par jour. Environ 2,3 pour cent des requêtes échouaient avec RemoteDisconnected. L'équipe était persuadée que les proxies étaient en cause. On a capturé un dump avec rotation de 100 Mo sur 24 heures, filtré tcp.flags.reset == 1 et regardé ip.src. Dans 91 pour cent des cas, c'était le client qui envoyait le RST. L'analyse des flux montrait : avant le RST, un silence de exactement 5 secondes après l'envoi de la requête, puis RST du client. Le code avait un read timeout de 5 secondes, alors que le serveur cible répondait en 6 à 8 secondes aux heures de pointe. Passer le timeout à 15 secondes a fait chuter les erreurs à 0,3 pour cent. Les 0,3 pour cent restants étaient de vrais FIN du proxy, 160 à 200 ms après le ClientHello : le serveur rejetait périodiquement les connexions provenant d'une partie du pool d'adresses. Ces données ont été transmises au support, qui a confirmé le tableau d'après ses propres logs de la zone C.
Cas 2 : coupures de gros téléchargements pile toutes les 10 minutes
Un client téléchargeait des archives de 300 à 800 Mo via un proxy mobile et se plaignait de coupures au milieu. Le dump montrait des RST du proxy à des moments espacés d'exactement 600 secondes à la seconde près, indépendamment du début du téléchargement. La cause était la rotation d'IP programmée toutes les 10 minutes : au changement d'adresse, le proxy ferme les tunnels actifs. Solution : passer le port en rotation à la demande et déclencher le changement d'adresse entre deux téléchargements. Les coupures ont totalement disparu, deux heures de diagnostic ont remplacé des semaines d'échanges par email.
Cas 3 : le serveur est en panne, mais on croit que c'est le proxy
Le matin, toutes les requêtes vers une même API se sont mises à renvoyer une erreur de connexion. Logs client : Connection aborted. Première réaction : le proxy est tombé. Un dump d'une minute a montré : le TCP avec le proxy s'établit en 38 ms, le CONNECT part, et 30 secondes plus tard arrivent un 504 et un FIN. En parallèle, une requête vers un autre hôte via le même proxy passait en 300 ms. Diagnostic : le serveur cible n'accepte pas les connexions du proxy. Quarante minutes plus tard, la page de statut du service cible confirmait l'incident de son côté. Le dump a économisé une matinée et évité un faux ticket au support du proxy.
Cas 4 : des pertes Wi-Fi prises pour un problème de proxy
Un développeur testait une intégration depuis son portable via un proxy mobile Proxeon et subissait des timeouts sporadiques. Le dump montrait des retransmissions du client à hauteur de 6 pour cent et des ACK dupliqués venant du proxy, sans aucun RST du proxy. Brancher le portable en câble a ramené les retransmissions à zéro, les timeouts ont disparu. Le problème venait du point d'accès surchargé du bureau.
Checklist pour capturer un dump destiné au support
Si vous comptez envoyer un dump au support d'un service proxy, voici l'ordre des opérations qui fera gagner du temps aux deux parties.
Préparation
- Synchronisez l'horloge de la machine via NTP. Vérifiez avec timedatectl ou date -u.
- Si possible, créez des identifiants proxy de test le temps du diagnostic, ou prévoyez de changer le mot de passe après l'envoi du dump.
- Notez la version du client, la bibliothèque HTTP, l'OS, le mode de connexion Internet (câble, Wi-Fi, réseau cellulaire).
- Notez l'adresse et le port du proxy, l'hôte cible, le comportement attendu et le comportement réel.
Capture
- Lancez tcpdump avec un filtre sur l'hôte et le port du proxy, avec rotation et taille de paquet complète :
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'- Reproduisez le problème. S'il est rare, laissez la capture tourner ; 48 fichiers de 5 minutes couvrent 4 heures.
- En parallèle de la capture, faites une requête de contrôle via curl avec -v et --trace-time, conservez la sortie. Elle donnera une correspondance temporelle précise avec les paquets.
- Notez l'heure exacte (UTC) de chaque manifestation de l'erreur dans les logs client.
- Arrêtez tcpdump avec Ctrl+C. Vérifiez que les fichiers ne sont pas vides : capinfos proxy-*.pcap.
Traitement
- Fusionnez les fichiers : mergecap -w all.pcapng proxy-*.pcap.
- Trouvez les flux problématiques avec le filtre tcp.flags.reset == 1 ou par l'heure issue des logs.
- Extrayez uniquement les flux utiles : tshark -r all.pcapng -Y 'tcp.stream in {12 47 93}' -w report.pcapng. Le support n'a pas besoin de heures de trafic normal.
- Vérifiez que le dump extrait ne contient rien de superflu : connexions étrangères, trafic d'autres services.
- Ne joignez pas le fichier de clés TLS.
Texte de la demande
- Heure de manifestation en UTC, à la seconde près.
- Adresse et port du proxy, hôte cible.
- Brève interprétation : ce que vous voyez dans le dump (par exemple 200 sur le CONNECT, puis FIN du proxy 170 ms après le ClientHello, RTT jusqu'au proxy 45 ms).
- Numéros de trames ou de flux dans le fichier joint.
- Sortie de curl -v avec horodatages.
- Ce que vous avez déjà vérifié et exclu : un autre hôte via le même proxy, le même hôte via une autre connexion.
Une demande comme celle-là est traitée par le support en une seule passe. Il lui suffit de recouper vos horodatages avec les logs de la zone C et de confirmer ou d'infirmer votre hypothèse.
FAQ
Pourquoi le dump ne contient-il pas l'adresse IP du serveur cible, alors que je m'adresse à lui ?
Parce que vous ne vous adressez pas à lui directement, mais via le proxy. Votre client établit une connexion TCP avec l'adresse du proxy et lui demande de se connecter à l'hôte cible. La connexion proxy - serveur n'existe que du côté du proxy. Dans le dump, le nom de l'hôte cible est visible dans la ligne CONNECT et dans le SNI à l'intérieur du ClientHello, mais aucun paquet vers son IP ne part de votre machine, et il ne peut pas y en avoir.
Peut-on, à partir du dump, savoir quelle IP externe le proxy a utilisée pour contacter le serveur ?
Non. Cette information relève de la zone C. Le seul moyen est d'interroger via le proxy un service qui renvoie l'adresse du client, ou de regarder dans l'espace client du fournisseur quelle adresse était active à ce moment.
Le proxy a envoyé un RST. Donc le problème vient du proxy ?
Pas nécessairement. Un RST provenant de l'adresse du proxy signifie que le tunnel a été fermé côté proxy, et la cause peut être dans le serveur cible ou dans le réseau derrière le proxy. Regardez le timing : si le RST arrive après un délai nettement supérieur au RTT jusqu'au proxy après votre dernier paquet, le proxy a probablement relayé la réaction du serveur. Si le RST arrive à un moment fixe ou après un intervalle précis, la politique du proxy est probable, par exemple une rotation ou une limite.
Comment mesurer le RTT jusqu'au proxy à partir du dump ?
Par la différence de temps entre le SYN de votre côté et le SYN-ACK du proxy au début de n'importe quelle connexion. Dans Wireshark, on peut activer Statistics > TCP Stream Graphs > Round Trip Time pour un flux, ou utiliser le champ tcp.analysis.ack_rtt comme colonne.
Wireshark n'affiche pas le TLS dans le tunnel, seulement des données TCP. Que faire ?
Faites un clic droit sur le paquet après la réponse 200 Connection established, choisissez Decode As, et dans la colonne Current sélectionnez TLS pour le port TCP du proxy. Vérifiez aussi que les paquets ne sont pas tronqués (-s 0 à la capture) et que le dissecteur HTTP est configuré sur le port du proxy s'il est non standard : Edit > Preferences > Protocols > HTTP > TCP ports.
En quoi cette approche diffère-t-elle de mitmproxy ?
mitmproxy travaille au niveau applicatif : il termine le TLS avec un certificat factice, lit et peut modifier les requêtes HTTP. Pour cela, le client doit faire confiance à son certificat racine. tcpdump et Wireshark travaillent au niveau paquet et ne substituent rien : vous voyez les vrais paquets, les vrais flags et les vrais timings, y compris les erreurs TCP que mitmproxy masquerait parce qu'il les aurait gérées lui-même. Pour diagnostiquer des coupures, le niveau paquet est plus honnête. Pour visualiser le contenu des requêtes, mitmproxy est plus simple, mais si besoin, on peut aussi voir le contenu de son propre trafic dans Wireshark via SSLKEYLOGFILE sans substituer de certificat.
Peut-on déchiffrer dans Wireshark le trafic que le proxy échange avec le serveur ?
Non. Vous n'avez ni les paquets de cette connexion, ni les clés. SSLKEYLOGFILE ne fournit les clés que des sessions de votre client, et à l'intérieur du tunnel CONNECT, c'est justement votre session avec le serveur, donc celle-là est déchiffrable. Mais il n'y a pas de TLS séparé entre le proxy et le serveur dans le cas d'un CONNECT : le proxy se contente de relayer vos octets.
Comment savoir si le client fuit en contournant le proxy ?
Capturez sans filtre sur le proxy, mais avec un filtre sur toute votre interface, et regardez les connexions sortantes vers les ports 80 et 443 à des adresses différentes du proxy, ainsi que l'UDP 443 (QUIC) et les requêtes DNS vers des résolveurs externes avec les noms des hôtes cibles. Filtre Wireshark : (tcp.port == 443 || udp.port == 443) && !(ip.addr == 203.0.113.10). Toute correspondance est une fuite de configuration.
Quelle taille doit faire un dump pour le support ?
Le plus petit possible. Un flux problématique extrait pèse de 5 à 500 Ko. Un fichier de plus de 50 Mo prendra plus de temps à analyser, et la moitié du volume sera du trafic normal. Extrayez les flux utiles avec tshark ou File > Export Specified Packets dans Wireshark.
Que faire si tcpdump sur une machine virtuelle affiche des paquets plus gros que le MTU ?
C'est le generic segmentation offload : le noyau transmet à tcpdump de gros segments avant que la carte réseau ne les découpe. Cela n'affecte pas l'analyse des flags, des timings et des coupures. Si ça gêne, désactivez l'offload le temps de la capture avec ethtool -K eth0 gso off tso off gro off, mais sachez que cela réduira les performances réseau.
Conclusion
Un dump de trafic via proxy, ce n'est pas de l'espionnage de contenu ni de la magie. C'est un enregistrement honnête de ce qui s'est réellement passé sur le fil : à la milliseconde près et pour chaque flag. Les logs du client disent que la connexion a été coupée. Le dump dit qui l'a fait, quand exactement, et ce qui s'est passé avant.
Résumons la méthode. Sur la machine cliente, vous ne voyez que la connexion jusqu'au proxy : handshake TCP, CONNECT ou dialogue SOCKS, et enregistrements TLS chiffrés dans le tunnel. Le serveur cible n'est pas visible directement, mais son comportement est transmis par le proxy via les codes de réponse au CONNECT, les timings et la manière de fermer le tunnel. Un RST ou FIN depuis votre adresse, c'est votre problème ou votre timeout. Des retransmissions et des ACK dupliqués sans fermeture, ce sont des pertes sur le chemin jusqu'au proxy. Une fermeture depuis le proxy après un délai nettement supérieur au RTT jusqu'à lui, c'est la réaction du serveur relayée. Une fermeture à des moments fixes, c'est la politique du proxy. Un Zero Window de votre côté, c'est votre application qui ne lit pas.
Les gestes à faire dès aujourd'hui : mettez en favori les commandes tcpdump avec rotation et les filtres Wireshark de cet article ; vérifiez que les horloges des machines qui font tourner vos clients sont synchronisées ; assurez-vous que votre client ne fuit pas en QUIC en contournant le proxy ; créez des identifiants de test pour le diagnostic, afin de ne pas exposer vos identifiants de production dans les dumps. Et la prochaine fois que les logs diront connection reset by peer, ne devinez pas. Capturez un dump, ouvrez le flux, regardez la direction et l'heure. Dans cinq minutes, vous saurez à qui écrire : à vous-même, au support du proxy ou au propriétaire du serveur cible.