Conectar ao broker MQTT com Python paho e TLS

Python paho e TLS | IoT | Blynk, IoT e MQTT

Mostrei aqui como configurar certificado no broker Mosquitto e mostrei como conectar ao broker utilizando TLS com mosquitto-clients. Também escrevi um artigo disponibilizando um script para coleta. E como montar um belo dashboard com Grafana e MQTT. Também como configurar HTTPS no Grafana; nada mal, hum? Mas ainda faltam alguns artigos para não ter desculpas para não usar MQTT. Antes de irmos para C++ e Qt, vamos ver como conectar ao broker MQTT com Python paho e TLS.

Broker MQTT

O processo de configuração de um broker MQTT é simples, como já demonstrado. Se ainda não viu nada sobre MQTT, talvez esteja um bocado atrasado; se bem que na verdade estamos todos. O MQTT (Message Queue Telemetry Transport) é um protocolo de telemetria criado pela IBM em 1999. Atualmente é um protocolo de grande importância, sendo provavelmente o mais leve depois da comunicação socket sem protocolos adicionais ao TCP/IP. Já escrevi “montanhas” de artigos sobre sua utilização, mas resolvi dedicar alguns artigos agora a compartilhar informações relacionadas à segurança – ainda mais que estou iniciando a implementação de LoRaWAN, de modo que isso não deve passar despercebido. Inicialmente não tinha mesmo a questão de um canal criptografado, mas agora como a temos, eis que para linguagem interpretada temos o Python paho e TLS.

Python paho e TLS: Python 2.7 ou Python 3?

Meu caro, esqueça o Python 2. Tem distribuição Linux que nem vem mais instalado, não vale o esforço de manter código nesse padrão. Algumas coisas mudaram o comportamento, como o range, print e outros. Até a forma de tratar hexadecimal mudou, como já mostrei em algum artigo tratando dados binários. Então, a recomendação é: use Python 3.

Código de exemplo

Claro que o importante é exemplificar, certo? Então permita-me contar brevemente a razão desse código que disponho logo após.

Pode ser que você se depare com o mesmo problema, mas tomara que não, senão valerá a máxima de que “os fins justificam os meios”.

Em alguns clientes que rodam um sistema no Raspberry, os reports do MQTT pararam de acontecer repentinamente. Após analisar um bocado, vi que a razão era bloqueio pelo próprio fornecedor do link, que bloqueou o serviço na porta (inicialmente) 1883, logo no início da implementação. A porta 8883 ainda não estava sequer habilitada, mas tomaria o mesmo caminho, a partir do momento em que fosse filtrado o tráfego. Como resolver isso? Bem, considerando que o servidor que recebia os dados MQTT era o mesmo que servia o Grafana, não daria pra fazer aplicar a solução sem outro servidor.

Após contratar um segundo servidor, a solução: encaminhar os dados do MQTT para a porta 443, já com TLS. Agora está resolvido o problema, mas teve custo. Se for contratar link e tiver a opção de escolher, tire a Copel da lista. Agora, ao código.

#!/usr/bin/env python3
import paho.mqtt.client as mqtt
import datetime
import time
import sys
import ssl
from influxdb import InfluxDBClient

def on_connect(client, userdata, flags, rc):
    #print("Connected with result code "+str(rc))
    client.subscribe("/TOPICO_RAIZ_A_SER_LIDO/#")
    
def on_message(client, userdata, msg):
    #print("Received a message on topic: " + msg.topic)
    # Use utc as timestamp
    receiveTime=datetime.datetime.utcnow()
    message=msg.payload.decode("utf-8")
    isfloatValue=False
    try:
        # Convert the string to a float so that it is stored as a number and not a string in the database
        val = float(message)
        isfloatValue=True
    except:
        #print("Could not convert " + message + " to a float value")
        isfloatValue=False

    if isfloatValue:
        #print(str(receiveTime) + ": " + msg.topic + " " + str(val))

        json_body = [
            {
                "measurement": msg.topic,
                "time": receiveTime,
                "fields": {
                    "value": val
                }
            }
        ]

        dbclient.write_points(json_body)
        #print("Finished writing to InfluxDB")
        
# Set up a client for InfluxDB
dbclient = InfluxDBClient('127.0.0.1', 8086, 'USER', 'SENHA', 'BANCO')

# Initialize the MQTT client that should connect to the Mosquitto broker
client = mqtt.Client("Colector")
client.on_connect = on_connect
client.on_message = on_message
client.username_pw_set('USERNAME_MQTT','SENHA_MQTT')

client.tls_set('/CAMINHO_ABSOLUTO_DO_CA_CRT')
client.tls_insecure_set(True)

connOK=False
while(connOK == False):
    try:
        client.connect("IP_OU_URL_DO_BROKER", 443, 60)
        print("Ok")
        connOK = True
    except:
        connOK = False
        print("ops...")
    time.sleep(2)

# Blocking loop to the Mosquitto broker
client.loop_forever()

Esse é o programa da bridge, que discorri a respeito no artigo sobre a implementação do Grafana, citado no primeiro parágrafo. Adicionalmente temos duas linhas relacionadas ao TLS. Em uma delas, definimos o caminho absoluto do arquivo .crt (descrito na implementação de TLS no MQTT, também citado no primeiro parágrafo). Na outra linha, definimos o parâmetro “insecure”, porque o nome do certificado não casa com o nome do broker, mas independente disso, sim, está com TLS e é uma conexão segura a partir de então.

Hora de conectar os pequeninos também a esse broker. É só acompanhar os próximos artigos!

 

Revisão: Ricardo Amaral de Andrade