Python Paho MQTT client self-signed certificates websockets, howto

Using Paho in Python with websockets and self-signed certificates for a https:// websocket connection has a couple of pitfalls, so I’m addressing this with some sample code here:

import ssl
import time
import paho.mqtt.client as mqtt

class PCPMQTTClient:
     def on_connect(self, client, userdata, flags, rc):
         self.connection_status = rc
         if rc == 0:
             self.connected_flag = True
             self.connection_error_flag = False
         else:
             self.connected_flag = False
             self.connection_error_flag = True

    # called when a mesage that was to be sent using publish()
     # has completed transmission to the broker
     # for qos=0 -> message has left the client
     # for qos=1 & 2 -> handshakes have been completed.
     # mid variable matches mid variable which publish() returns
     def on_publish(self, client, userdata, mid):
         if self.verbose:
             print(“publish 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):
         if test:
             print(“Connecting to MQTT”, end=”)
         self.connection_status = -1  # no connection attempts yet
         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()  # according to Steve better AFTER connect
         continue_flag = False
         keep_connection_flag = not test
         timeout_counter = 0
         while not continue_flag:
             time.sleep(1)
             timeout_counter += 1
             if test:
                 print(“.”, end=”)  # end=” will suppress newline
             if self.connected_flag:
                 continue_flag = True
             if timeout_counter > 6:
                 continue_flag = True
                 keep_connection_flag = False
         code = self.connection_status
         success = False
         if code == 0:
             success = True
             message = “Connection successful!”
             self.connected_flag = True
             if not keep_connection_flag:  # necessary to be this, to detect if we are not able to connect!
                 self.disconnect()  # clean disconnect from server
         elif code == -1:
             message = “Connection timed out – server not online? Certificate error (try –selfsigned)?”
         elif code == 1:
             message = “Connection refused – incorrect protocol version”
         elif code == 2:
             message = “Connection refused – invalid client identifier”
         elif code == 3:
             message = “Connection refused – server unavailable”
         elif code == 4:
             message = “Connection refused – bad username or password”
         elif code == 5:
             message = “Connection refused – not authorised”
         else:
             message = “Another error ” + str(code) + ” occured, please check the Paho documentation”
         print(”)
         return {“success”: success, “code”: code, “message”: message}

    def publish(self, topic, message, qos=0, retain=False):
         success = False
         code = ‘PCP_WRONG_INPUTFORMAT’
         error_message = ‘Wrong input format of data (e.g. topic cannot be Null)’
         if topic:
             pubresult = self.mqttc.publish(
                 topic=topic,
                 payload=message,
                 qos=qos,
                 retain=retain)
             # process pubresult
             if pubresult.rc == mqtt.MQTT_ERR_SUCCESS:
                 code = ‘MQTT_ERR_SUCCESS’
                 error_message = ‘Successfully sent message to broker.’
                 success = True
             elif pubresult.rc == mqtt.MQTT_ERR_NO_CONN:
                 code = ‘MQTT_ERR_NO_CONN’
                 error_message = ‘No connection to MQTT server, try to reconnect’
             elif pubresult.rc == mqtt.MQTT_ERR_QUEUE_SIZE:
                 code = ‘MQTT_ERR_QUEUE_SIZE’
                 error_message = ‘Message is neither queued nor sent.’

        return {“success”: success, “code”: code, “message”: error_message, “mid”: pubresult.mid}

    def disconnect(self):
         # as per 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=clean_session,
             userdata=None,
             protocol=mqtt.MQTTv311,
             transport=connectiondata[‘transport’])
         self.connectiondata = connectiondata
         self.connected_flag = False
         self.mqttc.on_connect = self.on_connect
         self.mqttc.on_publish = self.on_publish
         if self.verbose:
             print(“### enabling logging for MQTT Client ###”)
             self.mqttc.on_log = self.on_log
         self.mqttc.username_pw_set(
             username=connectiondata[‘username’],
             password=connectiondata[‘password’])
         if connectiondata[‘transport’] == “websockets”:
             self.mqttc.ws_set_options(path=self.connectiondata[‘path’])
         if self.verbose:
             print(“### connecting with the following parameters ###”)
             print(“* client_id: ” + connectiondata[‘client_id’])
             print(“* clean_session: ” + str(clean_session))
             print(“* transport: ” + connectiondata[‘transport’])
             print(“* path: ” + connectiondata[‘path’])
             print(“* username: ” + connectiondata[‘username’])
             print(“* password: (not being shown!)”)
         # self.mqttc.tls_set_context()
         if insecure:
             self.mqttc.tls_set(cert_reqs=ssl.CERT_NONE)
             if self.verbose:
                 print(“### insecure operation mode (–selfsigned) set! ###”)
             self.mqttc.tls_insecure_set(True)
         else:
             self.mqttc.tls_set()

As you can see, it is important to do TWO things for an insecure connection:

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

The tls_insecure_set(True) call will allow you to omit certificate name matching. It will still try to ensure that the certificate is valid as per the certificate chain!

Thus it is a bit of a misnomer, I guess.

The sample code shown above is part of a development on picockpit-client, for the picockpit.com platform

Paho / Websockets

connectiondata[‘transport’] is set elsewhere in the code. It can be either (a string) “tcp” or “websockets”.

If you specify websockets, you can additionally use ws_set_options to set a path, for example “/mqtt”.

            host=self.connectiondata[‘host’],
             port=self.connectiondata[‘port’],

the host is your webhost – without http / https prefix. For local development, for instance, I use “picockpit.local” (proxied through envoy, see my other articles).

the port, in the case of a TLS / SSL connection over the default https port is 443

Suggestion for easier debugging:

For better debugging switch from the async connection code to a synchronious connection (replace connect_async):

        self.mqttc.connect(
            host=self.connectiondata[‘host’],
            port=self.connectiondata[‘port’],
            keepalive=60,
            bind_address=””)

References: