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 mqttclass 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 = Falsedef __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:
- https://github.com/eclipse/paho.mqtt.python/issues/85
- https://github.com/eclipse/paho.mqtt.python/issues/148
- https://aws.amazon.com/blogs/iot/how-to-implement-mqtt-with-tls-client-authentication-on-port-443-from-client-devices-python/
- https://www.eclipse.org/paho/clients/python/docs/ – the official Paho Python documentation