Python Paho MQTT cliente websockets certificados autofirmados, howto

El uso de Paho en Python con websockets y certificados autofirmados para una conexión de websocket https:// tiene un par de escollos, por lo que estoy abordando esto con un código de ejemplo aquí:

importar ssl
tiempo de importación
importar paho.mqtt.client como mqtt

clase PCPMQTTClient:
     def on_connect(self, client, userdata, flags, rc):
         self.connection_status = rc
         si rc == 0:
             self.connected_flag = True
             self.connection_error_flag = False
         Si no:
             self.connected_flag = False
             self.connection_error_flag = True

    # llamado cuando un mensaje que iba a ser enviado mediante publish()
     # ha completado la transmisión al corredor
     # para qos=0 -> el mensaje ha salido del cliente
     # para qos=1 y 2 -> los apretones de manos se han completado.
     # variable media coincide con la variable media que devuelve publish()
     def on_publish(self, client, userdata, mid):
         si self.verbose:
             print("publicar callback mid: " + str(mid))

    def on_log(client, userdata, level, buf):
         print(buf)

    def testconnection(self):
         return self.connect(test=True)

    def connect(self, test=False):
         si la prueba:
             print("Conectando a MQTT", end=")
         self.connection_status = -1 # no hay intentos de conexión todavía
         self.connection_error_flag = False
         self.mqttc.connect_async(
             host=self.connectiondata['host'],
             port=self.connectiondata['port'],
             keepalive=60,
             bind_address="")

        self.mqttc.loop_start() # según Steve mejor AFTER connect
         continue_flag = False
         keep_connection_flag = no test
         timeout_counter = 0
         mientras no continue_flag:
             time.sleep(1)
             timeout_counter += 1
             si la prueba:
                 print(".", end=") # end=" suprimirá la nueva línea
             si self.connected_flag:
                 continue_flag = True
             si timeout_counter > 6:
                 continue_flag = True
                 keep_connection_flag = False
         code = self.connection_status
         éxito = Falso
         si código == 0:
             éxito = Verdadero
             mensaje = "¡Conexión exitosa!"
             self.connected_flag = True
             ¡si no keep_connection_flag: # necesario ser esto, para detectar si no somos capaces de conectar!
                 self.disconnect() # desconecta limpiamente del servidor
         elif code == -1:
             message = "¿Conexión agotada - servidor no conectado? ¿Error de certificado (intente -selfsigned)?"
         elif code == 1:
             mensaje = "Conexión rechazada - versión de protocolo incorrecta"
         elif code == 2:
             mensaje = "Conexión rechazada - identificador de cliente no válido"
         elif code == 3:
             mensaje = "Conexión rechazada - servidor no disponible"
         elif code == 4:
             mensaje = "Conexión rechazada - nombre de usuario o contraseña incorrectos"
         elif code == 5:
             mensaje = "Conexión rechazada - no autorizada"
         Si no:
             message = "Se ha producido otro error " + str(code) + ", por favor, consulte la documentación de Paho"
         print(")
         return {"éxito": éxito, "código": código, "mensaje": mensaje}

    def publish(self, topic, message, qos=0, retain=False):
         éxito = Falso
         code = 'PCP_WRONG_INPUTFORMAT'
         error_message = 'Formato de entrada de datos incorrecto (por ejemplo, el tema no puede ser Null)'
         si el tema:
             pubresultado = self.mqttc.publish(
                 topic=tema,
                 payload=mensaje,
                 qos=qos,
                 retener=retención)
             # proceso pubresultado
             si pubresult.rc == mqtt.MQTT_ERR_SUCCESS:
                 code = 'MQTT_ERR_SUCCESS'
                 error_message = 'Mensaje enviado con éxito al corredor.'
                 éxito = Verdadero
             elif pubresult.rc == mqtt.MQTT_ERR_NO_CONN:
                 code = 'MQTT_ERR_NO_CONN'
                 error_message = 'No hay conexión con el servidor MQTT, intente reconectarse'
             elif pubresult.rc == mqtt.MQTT_ERR_QUEUE_SIZE:
                 code = 'MQTT_ERR_QUEUE_SIZE'
                 error_message = 'El mensaje no está en cola ni se ha enviado'.

        return {"success": éxito, "code": código, "message": error_message, "mid": pubresult.mid}

    def disconnect(self):
         # según steve http://www.steves-internet-guide.com/client-connections-python-mqtt/
         self.mqttc.loop_stop()
         self.mqttc.disconnect()
         self.connected_flag = False

    def __init__(self, connectiondata, verbose=False, insecure=False):
         self.verbose = verbose
         clean_session = True
         self.mqttc = mqtt.Client(
             client_id=connectiondata['client_id'],
             clean_session=sesión limpia,
             userdata=Ninguna,
             protocol=mqtt.MQTTv311,
             transporte=conexióndatos['transporte'])
         self.connectiondata = connectiondata
         self.connected_flag = False
         self.mqttc.on_connect = self.on_connect
         self.mqttc.on_publish = self.on_publish
         si self.verbose:
             print("### habilitando el registro para el cliente MQTT ##")
             self.mqttc.on_log = self.on_log
         self.mqttc.username_pw_set(
             username=nombre de usuario['username'],
             contraseña=datosdeconexión['contraseña'])
         si connectiondata['transport'] == "websockets":
             self.mqttc.ws_set_options(path=self.connectiondata['path'])
         si self.verbose:
             print("### conectándose con los siguientes parámetros ###")
             print("* client_id: " + connectiondata['client_id'])
             print("* sesión_limpia: " + str(clean_session))
             print("* transporte: " + connectiondata['transporte'])
             print("* ruta: " + connectiondata['ruta'])
             print("* nombre de usuario: " + connectiondata['username'])
             print("* contraseña: (¡no se muestra!)")
         # self.mqttc.tls_set_context()
         si es inseguro:
             self.mqttc.tls_set(cert_reqs=ssl.CERT_NONE)
             si self.verbose:
                 print("¡### modo de operación inseguro (-selfsigned) establecido! ###")
             self.mqttc.tls_insecure_set(True)
         Si no:
             self.mqttc.tls_set()

Como puedes ver, es importante hacer DOS cosas para una conexión insegura:

  • tls_set(cert_reqs=ssl.CERT_NONE)
  • tls_insecure_set(True)

La llamada tls_insecure_set(True) le permitirá omitir la comprobación del nombre del certificado. Aún así, intentará asegurarse de que el certificado es válido según la cadena de certificados.

Por lo tanto, creo que es un nombre un poco equivocado.

El código de ejemplo que se muestra arriba es parte de un desarrollo en picockpit-client, para el Plataforma picockpit.com

Paho / Websockets

connectiondata['transport'] se establece en otra parte del código. Puede ser (una cadena) "tcp" o "websockets".

Si especifica websockets, puede utilizar adicionalmente ws_set_options para establecer una ruta, por ejemplo "/mqtt".

            host=self.connectiondata['host'],
             port=self.connectiondata['port'],

el host es su webhost - sin prefijo http / https. Para el desarrollo local, por ejemplo, utilizo "picockpit.local" (proxy a través de envoy, ver mis otros artículos).

el puerto, en el caso de una conexión TLS / SSL sobre el puerto https por defecto es 443

Sugerencia para facilitar la depuración:

Para una mejor depuración, cambie el código de conexión asíncrona por una conexión síncrona (sustituya connect_async):

        self.mqttc.connect(
            host=self.connectiondata['host'],
            port=self.connectiondata['port'],
            keepalive=60,
            bind_address="")

Referencias: