Tests de brokers

Diagnostiquer les problèmes réseau MQTT : DNS, pare-feu et proxys

Comprenez pourquoi votre client MQTT n'atteint pas le broker : testez DNS, routage TCP, pare-feu, proxys d'entreprise et interception TLS couche par couche.

Mis à jour · 9 min de lecture

Diagnostiquer les problèmes réseau MQTT : DNS, pare-feu et proxys

Quand un client MQTT échoue avec un vague « connection failed », la cause est rarement MQTT lui-même. C'est généralement l'une des quatre couches en dessous : le DNS n'a pas résolu le nom du broker, le TCP n'a pas atteint le port, un proxy ou un pare-feu a avalé la connexion sans rien dire, ou le TLS a été intercepté et le certificat ne correspond plus. Tester ces couches dans l'ordre — DNS → TCP → TLS → MQTT — permet de trouver la couche en panne en quelques minutes.

Ce guide vous donne la commande exacte pour chaque couche, les messages d'erreur produits par chaque panne et un script Python qui exécute toute la chaîne et indique où elle casse.

Lisez d'abord le message d'erreur

Le texte de l'erreur indique déjà la couche à examiner. Voici les messages les plus recherchés :

ErreurCoucheCause habituelle
getaddrinfo ENOTFOUND broker.connectmq.com (Node.js)DNSFaute de frappe dans l'hôte, pas de serveur DNS, DNS split-horizon
[Errno -2] Name or service not known (Python, Linux)DNSIdem
[Errno 8] nodename nor servname provided, or not known (Python, macOS)DNSIdem
getaddrinfo EAI_AGAINDNSRésolveur injoignable ou en échec temporaire
connect ETIMEDOUT / TimeoutError: timed outTCP / pare-feuPaquets jetés en route, mauvaise IP, port bloqué
connect ECONNREFUSED / [Errno 111] Connection refusedTCPHôte joignable, rien n'écoute sur ce port
ECONNRESET juste après la connexionProxy / pare-feuUn équipement intermédiaire a coupé la connexion après l'avoir inspectée
certificate verify failed: unable to get local issuer certificateTLSBundle de CA manquant ou interception TLS
self signed certificate in certificate chainTLSProxy d'entreprise qui re-signe le trafic

Si vous recevez déjà un CONNACK avec un reason code, le réseau fonctionne et le problème relève de l'authentification ou de la configuration — consultez la référence des reason codes.

Couche 1 : résolution DNS

Vérifiez que le nom se résout, et qu'il se résout vers les mêmes adresses partout :

Résoudre le nom du broker
dig +short broker.connectmq.com A
dig +short broker.connectmq.com AAAA

# Comparer avec un résolveur public pour repérer un split-horizon ou des enregistrements périmés
dig +short broker.connectmq.com @1.1.1.1

# Windows
nslookup broker.connectmq.com

Ce qu'il faut regarder :

  • Aucune réponse — le nom n'existe pas dans ce résolveur. Vérifiez l'orthographe et si un VPN ou le DNS d'entreprise remplace les zones externes.
  • Des réponses différentes entre votre résolveur et un résolveur public — DNS split-horizon, ancien enregistrement en cache après une migration de broker, ou filtre DNS qui renvoie l'IP d'une page de blocage.
  • Un enregistrement AAAA sans route IPv6 — le client essaie d'abord IPv6 et reste bloqué jusqu'au repli. Voir problèmes IPv6 avec MQTT.

Dans un conteneur, le DNS vient du runtime de conteneurs, pas de l'hôte. Lancez le même dig dans le conteneur (docker exec -it app sh) avant de supposer que le résultat de l'hôte s'applique.

Couche 2 : joignabilité TCP et routage

La résolution fonctionne ; ouvrez maintenant une connexion TCP brute vers chaque port MQTT que vous pourriez utiliser :

Tester les ports MQTT
nc -vz -w 5 broker.connectmq.com 1883   # MQTT sur TCP
nc -vz -w 5 broker.connectmq.com 8883   # MQTT sur TLS
nc -vz -w 5 broker.connectmq.com 8084   # MQTT sur WebSockets sécurisés
Windows PowerShell
Test-NetConnection broker.connectmq.com -Port 8883

La différence entre un timeout et un refus est le signal le plus utile du débogage réseau :

  • succeeded / TcpTestSucceeded : True — le chemin est ouvert ; passez au TLS.
  • Connection refused — vous avez atteint l'hôte, mais rien n'écoute. Mauvais port, ou broker arrêté.
  • Blocage puis timeout — quelque chose entre vous et le broker jette les paquets. C'est presque toujours un pare-feu.

Pour voir où les paquets s'arrêtent, tracez la route sur le vrai port, pas en ICMP (que beaucoup de réseaux traitent différemment) :

Tracer le chemin TCP
sudo traceroute -T -p 8883 broker.connectmq.com   # Linux
mtr --tcp --port 8883 broker.connectmq.com        # si mtr est installé

Si la trace s'arrête à votre propre passerelle ou au premier saut de l'entreprise, le blocage est local. Si elle s'arrête loin, le problème est en amont ou à la bordure du broker. Un pas-à-pas complet se trouve dans tester si un port MQTT est ouvert, et les ports standards dans ports MQTT.

Couche 3 : pare-feu et proxys

Les réseaux de bureau, d'hôtel et de cloud n'autorisent souvent que le HTTP(S) sortant, via un proxy explicite. Les clients MQTT ne lisent pas HTTP_PROXY par défaut, la connexion directe finit donc simplement en timeout.

Vérifiez d'abord si un proxy est configuré :

Chercher les paramètres de proxy
env | grep -i _proxy
# Windows : netsh winhttp show proxy

Vérifiez ensuite si le proxy accepte d'ouvrir un tunnel vers le port du broker avec un CONNECT HTTP :

Tunnel à travers un proxy HTTP
nc -X connect -x proxy.example.corp:3128 -vz broker.connectmq.com 8883

Si le proxy refuse CONNECT vers 8883 mais autorise 443, vous avez deux options : utiliser MQTT sur WebSockets sécurisés sur un port autorisé par le proxy, ou demander à l'équipe réseau d'autoriser l'hôte du broker. Les clients dans le navigateur passent automatiquement par le proxy système, c'est pourquoi le client MQTT en ligne fonctionne souvent là où un client natif échoue. D'autres stratégies dans MQTT derrière un pare-feu d'entreprise et MQTT sur le port 443.

Couche 4 : TLS et interception

Le port TCP est ouvert, mais le TLS échoue. Regardez le certificat que le client reçoit réellement :

Inspecter la chaîne de certificats
openssl s_client -connect broker.connectmq.com:8883 \
  -servername broker.connectmq.com -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Comparez la ligne issuer avec ce que vous obtenez depuis un partage de connexion mobile ou votre réseau domestique. Si, au bureau, l'émetteur est le nom de votre entreprise ou d'un éditeur de sécurité, un proxy à inspection TLS re-signe la connexion. Votre client MQTT la rejette avec certificate verify failed ou self signed certificate in certificate chain, ce qui est le comportement attendu.

Solutions, par ordre de préférence :

  1. Demandez l'exclusion de l'hôte du broker de l'inspection TLS (MQTT n'est pas du HTTP, et beaucoup de produits d'inspection le cassent de toute façon).
  2. Si la politique impose l'inspection, ajoutez la CA racine de l'entreprise au magasin de confiance du client — ne désactivez jamais la vérification du certificat.
  3. Assurez-vous que le client envoie le SNI (le -servername ci-dessus) : sans lui, certains endpoints renvoient un autre certificat, ce qui produit des erreurs de hostname mismatch.

Si le handshake reste bloqué au lieu d'échouer, surtout via un VPN, soupçonnez un problème de MTU : les petits paquets passent, mais le gros message du certificat est jeté. Un test avec un MTU plus petit sur l'interface le confirme. Les erreurs TLS en général sont traitées dans MQTT TLS handshake failed.

Exécuter toute la chaîne avec un script

Ce script (Python 3.8+, paho-mqtt 2.x) teste chaque couche dans l'ordre, s'arrête au premier échec et affiche la durée de chaque étape :

netcheck.py
import os, socket, ssl, sys, time
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 8883

def dns():
    infos = socket.getaddrinfo(HOST, PORT, proto=socket.IPPROTO_TCP)
    return ", ".join(sorted({i[4][0] for i in infos}))

def tcp():
    with socket.create_connection((HOST, PORT), timeout=5) as s:
        return f"connected to {s.getpeername()[0]}"

def tls():
    ctx = ssl.create_default_context()
    with socket.create_connection((HOST, PORT), timeout=5) as raw:
        with ctx.wrap_socket(raw, server_hostname=HOST) as s:
            issuer = dict(x[0] for x in s.getpeercert()["issuer"])
            return f"{s.version()}, issuer: {issuer.get('organizationName')}"

def mqtt_connect():
    result = {}
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
    c.on_connect = lambda cl, ud, flags, reason, props: result.setdefault("rc", reason)
    if os.getenv("MQTT_USER"):
        c.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
    c.tls_set()
    c.connect(HOST, PORT, keepalive=30)
    c.loop_start()
    for _ in range(50):
        if "rc" in result:
            break
        time.sleep(0.1)
    c.disconnect()
    c.loop_stop()
    if "rc" not in result:
        raise TimeoutError("no CONNACK within 5 s")
    if result["rc"].is_failure:
        raise RuntimeError(f"CONNACK: {result['rc']}")
    return f"CONNACK: {result['rc']}"

for name, step in [("DNS", dns), ("TCP", tcp), ("TLS", tls), ("MQTT", mqtt_connect)]:
    start = time.perf_counter()
    try:
        info = step()
    except Exception as e:
        print(f"FAIL  {name:<5} {type(e).__name__}: {e}")
        sys.exit(1)
    print(f"OK    {name:<5} {(time.perf_counter() - start) * 1000:6.0f} ms  {info}")

Une exécution saine affiche quatre lignes OK. Un échec indique la couche et l'exception exacte, qui correspond directement au tableau du début de cet article. Lancez-le sur la machine qui a le problème — un portable sur un autre réseau ne prouve rien sur l'appareil qui échoue.

Confirmer la correction

Après avoir modifié le DNS, les règles de pare-feu ou les paramètres de proxy, vérifiez de bout en bout plutôt que de faire confiance au changement :

  1. Relancez netcheck.py sur la machine concernée et vérifiez que les quatre couches passent.
  2. Abonnez-vous à un topic de test dans le client en ligne et publiez-y depuis l'appareil concerné.
  3. Laissez la connexion inactive plus longtemps que le délai supposé du pare-feu et vérifiez que les messages arrivent toujours.

Ouvrir le client en ligne pour tester la livraison

Les problèmes réseau semblent aléatoires tant qu'on ne les découpe pas en couches. Résolvez le nom, ouvrez le socket, inspectez le certificat, puis parlez MQTT : la première étape qui échoue est celle à corriger, et le message d'erreur la nomme généralement déjà.

Questions fréquentes

Pourquoi mon client MQTT fonctionne-t-il à la maison mais pas au bureau ?

Les réseaux d'entreprise bloquent souvent les ports sortants comme 1883 et 8883, imposent un proxy HTTP ou inspectent le TLS. Testez chaque couche depuis le réseau du bureau : DNS, une connexion TCP brute, l'émetteur du certificat TLS, puis MQTT. MQTT sur WebSockets via le port 443 est souvent la solution.

Comment savoir si un pare-feu bloque le trafic MQTT ?

Un pare-feu qui jette les paquets produit un timeout (ETIMEDOUT, 'timed out'), alors qu'un port fermé sur un hôte joignable répond immédiatement par 'Connection refused'. Lancez nc -vz ou Test-NetConnection sur le port et comparez avec un autre port du même hôte.

Comment détecter une interception TLS sur mon réseau ?

Lancez openssl s_client vers le broker et affichez l'émetteur du certificat. Si l'émetteur est l'autorité de votre entreprise ou d'un produit de sécurité au lieu d'une CA publique, la connexion est interceptée et votre client échouera à la vérification du certificat tant qu'il ne fait pas confiance à cette CA.