Pruebas de brokers

Diagnosticar problemas de red en MQTT: DNS, firewalls y proxies

Descubre por qué tu cliente MQTT no llega al broker: prueba DNS, rutas TCP, firewalls, proxies corporativos e intercepción TLS capa por capa con comandos.

Actualizado · 9 min de lectura

Diagnosticar problemas de red en MQTT: DNS, firewalls y proxies

Cuando un cliente MQTT falla con un vago "connection failed", la causa casi nunca es MQTT. Suele ser una de las cuatro capas que hay debajo: DNS no pudo resolver el nombre del broker, TCP no pudo alcanzar el puerto, un proxy o firewall se tragó la conexión en silencio, o el TLS fue interceptado y el certificado ya no coincide. Probar esas capas en orden — DNS → TCP → TLS → MQTT — encuentra la que falla en pocos minutos.

Esta guía te da el comando exacto para cada capa, los mensajes de error que produce cada fallo y un script en Python que ejecuta toda la cadena y te dice dónde se rompe.

Lee primero el mensaje de error

El texto del error ya te indica qué capa mirar. Estos son los mensajes que más se buscan:

ErrorCapaCausa habitual
getaddrinfo ENOTFOUND broker.connectmq.com (Node.js)DNSHost mal escrito, sin servidor DNS, DNS split-horizon
[Errno -2] Name or service not known (Python, Linux)DNSIgual que arriba
[Errno 8] nodename nor servname provided, or not known (Python, macOS)DNSIgual que arriba
getaddrinfo EAI_AGAINDNSResolver inalcanzable o con fallos temporales
connect ETIMEDOUT / TimeoutError: timed outTCP / firewallPaquetes descartados en el camino, IP errónea, puerto bloqueado
connect ECONNREFUSED / [Errno 111] Connection refusedTCPHost alcanzable, nada escuchando en ese puerto
ECONNRESET justo después de conectarProxy / firewallUn equipo intermedio cortó la conexión tras inspeccionarla
certificate verify failed: unable to get local issuer certificateTLSFalta el bundle de CAs o hay intercepción TLS
self signed certificate in certificate chainTLSProxy corporativo que vuelve a firmar el tráfico

Si ya recibes un CONNACK con un reason code, la red está bien y el problema es de autenticación o configuración: búscalo en la referencia de reason codes.

Capa 1: resolución DNS

Comprueba que el nombre se resuelve, y que se resuelve a las mismas direcciones en todas partes:

Resolver el nombre del broker
dig +short broker.connectmq.com A
dig +short broker.connectmq.com AAAA

# Compara con un resolver público para detectar split-horizon o registros obsoletos
dig +short broker.connectmq.com @1.1.1.1

# Windows
nslookup broker.connectmq.com

Qué buscar:

  • Sin respuesta: el nombre no existe en este resolver. Revisa la ortografía y si una VPN o el DNS corporativo sobrescriben zonas externas.
  • Respuestas distintas entre tu resolver y uno público: DNS split-horizon, un registro antiguo en caché tras una migración del broker o un filtro DNS que devuelve la IP de una página de bloqueo.
  • Un registro AAAA pero sin ruta IPv6: el cliente prueba IPv6 primero y se queda colgado hasta que hace fallback. Consulta problemas de IPv6 en MQTT.

Dentro de contenedores, el DNS lo proporciona el runtime, no el host. Ejecuta el mismo dig dentro del contenedor (docker exec -it app sh) antes de suponer que el resultado del host aplica.

Capa 2: alcance TCP y enrutamiento

La resolución funciona; ahora abre una conexión TCP directa a cada puerto MQTT que puedas usar:

Probar los puertos MQTT
nc -vz -w 5 broker.connectmq.com 1883   # MQTT sobre TCP
nc -vz -w 5 broker.connectmq.com 8883   # MQTT sobre TLS
nc -vz -w 5 broker.connectmq.com 8084   # MQTT sobre WebSockets seguros
Windows PowerShell
Test-NetConnection broker.connectmq.com -Port 8883

La diferencia entre un timeout y un rechazo es la señal más útil en la depuración de red:

  • succeeded / TcpTestSucceeded : True: el camino está abierto; pasa a TLS.
  • Connection refused: llegaste al host, pero nada escucha ahí. Puerto equivocado o broker caído.
  • Se queda colgado y luego da timeout: algo entre tú y el broker descarta los paquetes. Casi siempre es un firewall.

Para ver dónde se detienen los paquetes, traza la ruta en el puerto real, no con ICMP (que muchas redes tratan de otra manera):

Trazar la ruta TCP
sudo traceroute -T -p 8883 broker.connectmq.com   # Linux
mtr --tcp --port 8883 broker.connectmq.com        # si tienes mtr instalado

Si la traza muere en tu propio gateway o en el primer salto corporativo, el bloqueo es local. Si muere lejos, el problema está aguas arriba o en el borde del broker. Tienes un recorrido completo en comprobar si un puerto MQTT está abierto, y los números de puerto estándar en puertos MQTT.

Capa 3: firewalls y proxies

Las redes de oficinas, hoteles y nubes a menudo solo permiten salir HTTP(S) a través de un proxy explícito. Los clientes MQTT no leen HTTP_PROXY por defecto, así que la conexión directa simplemente da timeout.

Primero, averigua si hay un proxy configurado:

Buscar la configuración de proxy
env | grep -i _proxy
# Windows: netsh winhttp show proxy

Después comprueba si el proxy hará un túnel hasta el puerto del broker con un CONNECT HTTP:

Túnel a través de un proxy HTTP
nc -X connect -x proxy.example.corp:3128 -vz broker.connectmq.com 8883

Si el proxy rechaza CONNECT al 8883 pero permite el 443, tienes dos opciones: usar MQTT sobre WebSockets seguros en un puerto que el proxy permita, o pedir al equipo de red que autorice el host del broker. Los clientes basados en navegador pasan automáticamente por el proxy del sistema, por eso el cliente MQTT online suele funcionar en redes donde un cliente nativo falla. Tienes más estrategias en MQTT detrás de un firewall corporativo y MQTT por el puerto 443.

Capa 4: TLS e intercepción

El puerto TCP está abierto, pero el TLS falla. Mira el certificado que el cliente recibe realmente:

Inspeccionar la cadena de certificados
openssl s_client -connect broker.connectmq.com:8883 \
  -servername broker.connectmq.com -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Compara la línea issuer con lo que obtienes desde el hotspot del móvil o la red de casa. Si en la oficina aparece como emisor el nombre de tu empresa o de un proveedor de seguridad, un proxy con inspección TLS está volviendo a firmar la conexión. Tu cliente MQTT la rechaza con certificate verify failed o self signed certificate in certificate chain, y es el comportamiento correcto.

Soluciones, por orden de preferencia:

  1. Pide que el host del broker se excluya de la inspección TLS (MQTT no es HTTP, y muchos productos de inspección lo rompen de todos modos).
  2. Si la política exige inspección, añade la CA raíz corporativa al almacén de confianza del cliente; nunca desactives la verificación del certificado.
  3. Asegúrate de que el cliente envía SNI (el -servername de arriba): sin él, algunos endpoints devuelven otro certificado, lo que produce errores de hostname mismatch.

Si el handshake se queda colgado en lugar de fallar, sobre todo en una VPN, sospecha de un problema de MTU: los paquetes pequeños pasan, pero el mensaje grande del certificado se descarta. Probar con un MTU menor en la interfaz lo confirma. Los errores de TLS en general se tratan en MQTT TLS handshake failed.

Ejecuta toda la cadena con un script

Este script (Python 3.8+, paho-mqtt 2.x) prueba cada capa en orden, se detiene en el primer fallo e imprime el tiempo de cada paso:

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}")

Una ejecución sana imprime cuatro líneas OK. Un fallo te indica la capa y la excepción exacta, que corresponde directamente a la tabla del inicio de este artículo. Ejecútalo en la máquina que tiene el problema: un portátil en otra red no demuestra nada sobre el dispositivo que falla.

Confirma la solución

Después de cambiar el DNS, las reglas del firewall o la configuración del proxy, verifica de extremo a extremo en lugar de fiarte del cambio:

  1. Vuelve a ejecutar netcheck.py en la máquina afectada y confirma que las cuatro capas pasan.
  2. Suscríbete a un topic de prueba en el cliente online y publica en él desde el dispositivo afectado.
  3. Deja la conexión inactiva más tiempo que el timeout sospechado del firewall y comprueba que los mensajes siguen llegando.

Abre el cliente online para probar la entrega

Los problemas de red parecen aleatorios hasta que los separas en capas. Resuelve el nombre, abre el socket, inspecciona el certificado y después habla MQTT: el primer paso que falle es el que hay que arreglar, y el mensaje de error normalmente ya lo nombra.

Preguntas frecuentes

¿Por qué mi cliente MQTT funciona en casa pero no en la oficina?

Las redes corporativas suelen bloquear puertos salientes como 1883 y 8883, obligan a pasar por un proxy HTTP o inspeccionan el TLS. Prueba cada capa desde la red de la oficina: DNS, una conexión TCP directa, el emisor del certificado TLS y luego MQTT. MQTT sobre WebSockets en el puerto 443 suele ser la salida.

¿Cómo sé si un firewall está descartando el tráfico MQTT?

Un firewall que descarta paquetes produce un timeout (ETIMEDOUT, 'timed out'), mientras que un puerto cerrado en un host alcanzable responde al instante con 'Connection refused'. Ejecuta nc -vz o Test-NetConnection contra el puerto y compáralo con otro puerto del mismo host.

¿Cómo detecto la intercepción TLS en mi red?

Ejecuta openssl s_client contra el broker e imprime el emisor del certificado. Si el emisor es la CA de tu empresa o de un producto de seguridad en lugar de una CA pública, la conexión está siendo interceptada y tu cliente fallará la verificación del certificado salvo que confíe en esa CA.