Testes de brokers

Como testar uma conexão MQTT com TLS (openssl, mosquitto, código)

Teste MQTT sobre TLS na porta 8883: openssl s_client, cadeia de certificados, SNI e verificação de hostname, mosquitto --cafile e correções para erros comuns.

Atualizado · 9 min de leitura

Como testar uma conexão MQTT com TLS (openssl, mosquitto, código)

Para testar uma conexão MQTT com TLS, verifique três camadas em ordem: o próprio handshake TLS com openssl s_client, depois uma sessão MQTT real com mosquitto_sub ou mosquitto_pub e, por fim, a biblioteca cliente que os seus dispositivos usam. Cada camada pode falhar por motivos diferentes, e testá-las separadamente mostra exatamente qual está quebrada.

Todos os exemplos usam broker.connectmq.com na porta 8883 (MQTT sobre TLS). Troque o host pelo do seu broker.

Passo 1: verifique o handshake com openssl

O openssl s_client fala TLS, mas não MQTT — exatamente o que você quer: ele mostra o certificado que o broker envia e se a sua máquina confia nele, sem nenhuma biblioteca MQTT no caminho.

Verificação TLS básica
openssl s_client -connect broker.connectmq.com:8883 \
  -servername broker.connectmq.com \
  -verify_hostname broker.connectmq.com \
  -showcerts </dev/null

O que cada opção faz:

  • -servername envia o SNI (Server Name Indication). Brokers e load balancers que hospedam vários nomes usam isso para escolher o certificado. Sem ele, você pode receber um certificado padrão e um erro de hostname.
  • -verify_hostname faz o openssl verificar se o certificado é válido para esse nome — a mesma verificação que o seu cliente MQTT faz.
  • -showcerts imprime todos os certificados enviados pelo servidor, para você ver se o intermediário está incluído.
  • </dev/null fecha a conexão logo após o handshake, em vez de esperar entrada.

No fim da saída, procure:

Resultado saudável
Verify return code: 0 (ok)

Qualquer outra coisa é um problema que você também verá no seu cliente MQTT:

Resultado do opensslSignificado
Verify return code: 20 (unable to get local issuer certificate)A CA (ou um intermediário ausente) não está no seu trust store
Verify return code: 18 (self-signed certificate)O certificado do servidor é autoassinado; passe a CA com -CAfile
Verify return code: 19 (self-signed certificate in certificate chain)A cadeia termina em uma raiz privada em que você não confia
Verify return code: 10 (certificate has expired)Renove o certificado no broker
Verify return code: 62 (hostname mismatch)O certificado não lista o nome ao qual você se conectou
wrong version numberEssa porta é MQTT sem criptografia, não TLS (geralmente 1883)
connect: Connection refused / timeoutPorta fechada ou filtrada — não é problema de TLS

Para um broker com CA privada, adicione -CAfile ca.crt e rode o comando de novo. Se ele retornar 0 (ok), seus certificados estão corretos e o cliente só precisa do mesmo arquivo de CA.

Inspecione o certificado rapidamente

Subject, issuer, SAN e validade
openssl s_client -connect broker.connectmq.com:8883 \
  -servername broker.connectmq.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

Confira se subjectAltName contém exatamente o hostname que os seus dispositivos usam. Clientes modernos ignoram o Common Name do certificado, então um nome que aparece só no CN não conta. Para uma verificação de CI que falhe duas semanas antes do vencimento, passe o certificado para openssl x509 -noout -checkend 1209600.

Para testar quais versões de TLS o broker aceita, repita o primeiro comando com -tls1_2 ou -tls1_3. Um dispositivo com uma pilha TLS antiga que só fala TLS 1.2 precisa que o broker mantenha o 1.2 ativado — veja TLS 1.2 vs 1.3 no MQTT.

Passo 2: rode uma sessão MQTT real sobre TLS

Um handshake bem-sucedido não prova que o listener MQTT por trás funciona. Assine e publique pela 8883:

Terminal 1 — assinatura sobre TLS
mosquitto_sub -h broker.connectmq.com -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u "$MQTT_USER" -P "$MQTT_PASS" \
  -t testmqtt/tls/check -v -d
Terminal 2 — publicação sobre TLS
mosquitto_pub -h broker.connectmq.com -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u "$MQTT_USER" -P "$MQTT_PASS" \
  -t testmqtt/tls/check -m "hello over tls"

Os clientes mosquitto só ativam TLS quando você informa uma opção de CA. Use --cafile com o seu bundle de CAs (o caminho acima é do Debian/Ubuntu; no macOS com Homebrew fica em $(brew --prefix)/etc/ca-certificates/cert.pem), --capath /etc/ssl/certs ou --tls-use-os-certs no mosquitto 2.x. Para uma CA privada, aponte --cafile para o seu ca.crt. Para TLS mútuo, adicione --cert client.crt --key client.key — veja certificados de cliente e mTLS.

Quando falha, o mosquitto é lacônico: imprime Error: A TLS error occurred. e nada mais. Volte ao passo 1 — o openssl diz por quê. Se o openssl funciona mas o mosquitto falha, compare o hostname: -h precisa ser o nome do certificado, não um endereço IP.

Passo 3: teste a partir do código do cliente

Por fim, teste com a biblioteca que a sua aplicação usa. O repositório de certificados e os padrões mudam de linguagem para linguagem, então uma conexão que funciona no mosquitto ainda pode falhar aqui.

tls_check.py (paho-mqtt 2.x)
import os
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, reason_code, properties):
    print("CONNACK:", reason_code)
    client.publish("testmqtt/tls/check", "hello from python over tls", qos=1)

c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
c.tls_set()  # CAs do sistema, verificação de hostname ativa; ca_certs="ca.crt" para uma CA privada
c.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
c.on_connect = on_connect
c.connect("broker.connectmq.com", 8883)
c.loop_forever()
tls-check.mjs (MQTT.js 5)
import mqtt from "mqtt";

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  username: process.env.MQTT_USER,
  password: process.env.MQTT_PASS,
  // ca: fs.readFileSync("ca.crt"),   // só para uma CA privada
  reconnectPeriod: 0,
});

client.on("connect", () => {
  console.log("connected over TLS");
  client.publish("testmqtt/tls/check", "hello from node over tls", () => client.end());
});
client.on("error", (err) => console.error(err.code, err.message));

Os erros que eles produzem são justamente os mais pesquisados:

BibliotecaErroCausa comum
Python[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificateCA ou intermediário ausente
Pythoncertificate verify failed: Hostname mismatch, certificate is not valid for '...'Conexão por IP ou nome errado
Python[SSL: WRONG_VERSION_NUMBER] wrong version numberCliente TLS em uma porta sem criptografia
Node.jsUNABLE_TO_VERIFY_LEAF_SIGNATURE — unable to verify the first certificateO servidor não envia o intermediário
Node.jsSELF_SIGNED_CERT_IN_CHAINCA privada não informada em ca
Node.jsERR_TLS_CERT_ALTNAME_INVALID — Hostname/IP does not match certificate's altnamesNome fora da lista SAN

As correções estão detalhadas uma a uma em certificate verify failed, falha no handshake TLS e hostname mismatch. O gerador de código cria trechos de conexão com TLS para outras linguagens.

Checklist rápido de testes TLS

  1. openssl s_client com -servername e -verify_hostname retorna Verify return code: 0 (ok).
  2. -showcerts lista o certificado do servidor e o intermediário.
  3. O SAN contém exatamente o hostname usado pelos clientes; o certificado não está perto de vencer.
  4. mosquitto_sub e mosquitto_pub trocam uma mensagem na 8883 com a verificação ativada.
  5. A biblioteca da sua aplicação conecta com o trust store padrão (ou com a CA privada que você distribui).
  6. A porta sem criptografia 1883 está fechada ou desativada em produção se TLS for obrigatório.
  7. Dispositivos com pilhas TLS antigas conectam com a versão de TLS e os cipher suites que você permite.

Quer também uma verificação pelo navegador? O cliente MQTT online conecta via WebSockets seguros na 8084, que em muitas configurações usa o mesmo certificado — um jeito rápido de confirmar o lado TLS do broker antes de depurar um dispositivo.

Testar TLS camada por camada transforma um vago "erro de TLS" em uma causa específica em poucos minutos. Mantenha o comando do openssl e a verificação de validade nos seus scripts e execute-os sempre que um certificado for renovado ou um load balancer mudar.

Perguntas frequentes

Como verifico se um broker MQTT suporta TLS?

Rode openssl s_client -connect host:8883 -servername host. Se o handshake funcionar, você verá a cadeia de certificados e 'Verify return code: 0 (ok)'. Um erro 'wrong version number' indica que a porta fala MQTT sem criptografia, não TLS.

Por que o TLS funciona no navegador mas falha no meu dispositivo?

Normalmente o servidor não envia o certificado intermediário. Navegadores conseguem buscar intermediários ausentes; openssl, mosquitto e pilhas TLS embarcadas não. Confira se s_client -showcerts lista o intermediário e corrija o arquivo de certificado do broker.

É seguro usar --insecure ou tls_insecure_set(True)?

Só como diagnóstico temporário na sua própria máquina. Essas opções desativam a verificação de hostname, o que deixa a conexão aberta a ataques man-in-the-middle. Corrija o certificado ou o hostname e mantenha a verificação ativada.