javascriptdockermosquittomqtt.js

mqtt over Secure WebSockets using mqtt.js


I have a eclipse-mosquitto container running on a server which I want to connect to over wss.

I have the following configuration on the server

allow_anonymous false
password_file /mosquitto/config/passwd

persistence true
persistence_location /mosquitto/data/

log_type all
log_dest file /mosquitto/log/mosquitto.log
log_dest stdout

listener 1883
protocol mqtt

listener 8083
protocol websockets
websockets_log_level all
socket_domain ipv4

listener 8883
protocol mqtt
require_certificate false
cafile /mosquitto/cert/ca.crt
certfile /mosquitto/cert/server.crt
keyfile /mosquitto/cert/server.key
tls_version tlsv1.2

listener 8084
protocol websockets
require_certificate false
cafile /mosquitto/cert/ca.crt
certfile /mosquitto/cert/server.crt
keyfile /mosquitto/cert/server.key
tls_version tlsv1.2
websockets_log_level all
socket_domain ipv4

user mosquitto

And the following docker-compose config

  mosquitto:
    container_name: mosquitto
    image: eclipse-mosquitto:latest
    restart: unless-stopped
    hostname: "${ENV_TYPE}-mosquitto"
    ports:
      - 1883:1883
      - 8883:8883
      - 8083:8083
      - 8084:8084
    volumes:
      - ./volumes/mosquitto/data:/mosquitto/data
      - ./volumes/mosquitto/log:/mosquitto/log
      - ./volumes/mosquitto/config:/mosquitto/config
      - ./volumes/mosquitto/cert:/mosquitto/cert

And using MQTT Explorer I am able to connect just fine over tls

mqtt conection

But when trying to connect to it using the mqtt.js library I fail to get a wss connection. ws over port 8083 works just fine, just as connecting to a a different broker over wss.

<body>
  <script src="https://unpkg.com/mqtt/dist/mqtt.js"></script>
  <script>
    const id = Math.random().toString(36).substring(7);
    const topic = "topic";
    const connection = "wss://username:password@37.97.203.138:8084"
    // const connection = "ws://username:password@37.97.203.138:8083" // Works
    // const connection = "wss://public:public@public.cloud.shiftr.io" // Works
    const client = mqtt.connect(connection, {
      rejectUnauthorized: false,
    });

    client.on("message", messageReceived);
    client.on("connect", function () {
      println("connected!");
      client.subscribe("topic");
      client.publish("topic", "Hello from HTML");
    });
    client.on("error", function (error) {
      println("Error: " + error);
    });
    client.on('end', function() {
      println("Disconnected");
    })


    function messageReceived(topic, message) {
      println(topic + ": " + message);
    }
    function println(message) {
      const p = document.createElement("p");
      p.textContent = message;
      document.querySelector("body").append(p);
    }
  </script>
</body>

edit:

The mosquito logs are not showing anything on these connection attempts.

from this post I added localStorage.debug = 'mqttjs*' to get more logging.

Which gave me these logs

mqttjs connecting to an MQTT broker... +0ms
mqtt.js:10115 mqttjs:client MqttClient :: version: +0ms 5.4.0
mqtt.js:10115 mqttjs:client MqttClient :: environment +0ms browser
mqtt.js:10115 mqttjs:client MqttClient :: options.protocol +0ms wss
mqtt.js:10115 mqttjs:client MqttClient :: options.protocolVersion +0ms 4
mqtt.js:10115 mqttjs:client MqttClient :: options.username +1ms mdd
mqtt.js:10115 mqttjs:client MqttClient :: options.keepalive +0ms 60
mqtt.js:10115 mqttjs:client MqttClient :: options.reconnectPeriod +0ms 5000
mqtt.js:10115 mqttjs:client MqttClient :: options.rejectUnauthorized +0ms false
mqtt.js:10115 mqttjs:client MqttClient :: options.properties.topicAliasMaximum +0ms undefined
mqtt.js:10115 mqttjs:client MqttClient :: clientId +0ms 4zjpti
mqtt.js:10115 mqttjs:client MqttClient :: setting up stream +0ms
mqtt.js:10115 mqttjs:client connect :: calling method to clear reconnect +0ms
mqtt.js:10115 mqttjs:client _clearReconnect : clearing reconnect timer +0ms
mqtt.js:10115 mqttjs:client connect :: using streamBuilder provided to client to create stream +0ms
mqtt.js:10115 mqttjs calling streambuilder for +1ms wss
mqtt.js:10115 mqttjs:ws browserStreamBuilder +0ms
mqtt.js:10115 mqttjs:client connect :: pipe stream to writable stream +1ms
mqtt.js:10115 mqttjs:client connect: sending packet `connect` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: packet: Object +0ms
mqtt.js:10115 mqttjs:client _writePacket :: emitting `packetsend` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writing to stream +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writeToStream result true +8ms
mqtt.js:21161 WebSocket connection to 'wss://37.97.203.138:8084/' failed: 
createBrowserWebSocket @ mqtt.js:21161
browserStreamBuilder @ mqtt.js:21185
wrapper @ mqtt.js:21398
connect @ mqtt.js:17840
_MqttClient @ mqtt.js:17816
connect @ mqtt.js:21400
(anonymous) @ examples/:13
mqtt.js:10115 mqttjs:ws WebSocket onError +21ms Event {isTrusted: true, type: 'error', target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}
mqtt.js:10115 mqttjs:ws WebSocket onClose +1ms CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: '', type: 'close', …}
mqtt.js:10115 mqttjs:client streamErrorHandler :: error +13ms WebSocket error
mqtt.js:10115 mqttjs:client noop :: +0ms Error: WebSocket error
    at WebSocket.onError (mqtt.js:21235:25)
examples/:27 error Error: WebSocket error
    at WebSocket.onError (mqtt.js:21235:25)
mqtt.js:10115 mqttjs:client end :: (4zjpti) +0ms
mqtt.js:10115 mqttjs:client end :: cb? false +0ms
mqtt.js:10115 mqttjs:client _clearReconnect : clearing reconnect timer +0ms
mqtt.js:10115 mqttjs:client end :: (4zjpti) :: immediately calling finish +0ms
mqtt.js:10115 mqttjs:client end :: (4zjpti) :: finish :: calling _cleanUp with force false +0ms
mqtt.js:10115 mqttjs:client _cleanUp :: done callback provided for on stream close +0ms
mqtt.js:10115 mqttjs:client _cleanUp :: forced? false +0ms
mqtt.js:10115 mqttjs:client _cleanUp :: (4zjpti) :: call _sendPacket with disconnect packet +0ms
mqtt.js:10115 mqttjs:client _sendPacket :: (4zjpti) ::  start +0ms
mqtt.js:10115 mqttjs:client _sendPacket :: client not connected. Storing packet offline. +0ms
mqtt.js:10115 mqttjs:client _storePacket :: packet: {cmd: 'disconnect'} +0ms
mqtt.js:10115 mqttjs:client _storePacket :: cb? true +0ms
mqtt.js:10115 mqttjs:client _cleanUp :: (4zjpti) :: removing stream `done` callback `close` listener +0ms
mqtt.js:10115 mqttjs:client end :: finish :: calling process.nextTick on closeStores +1ms
mqtt.js:10115 mqttjs:client (4zjpti)stream :: on close +0ms
mqtt.js:10115 mqttjs:client _flushVolatile :: deleting volatile messages from the queue and setting their callbacks as error function +0ms
mqtt.js:10115 mqttjs:client stream: emit close to MqttClient +0ms
mqtt.js:10115 mqttjs:client close :: connected set to `false` +0ms
mqtt.js:10115 mqttjs:client close :: clearing connackTimer +0ms
mqtt.js:10115 mqttjs:client close :: clearing ping timer +0ms
mqtt.js:10115 mqttjs:client close :: calling _setupReconnect +0ms
mqtt.js:10115 mqttjs:client _setupReconnect :: doing nothing... +0ms
mqtt.js:10115 mqttjs:client end :: closeStores: closing incoming and outgoing stores +0ms
mqtt.js:10115 mqttjs:client end :: closeStores: emitting end +0ms
mqtt.js:10115 mqttjs:client end :: closeStores: invoking callback with args +0ms
mqtt.js:10115 mqttjs:client noop :: +0ms undefined

and logging when connecting to wss://public:public@public.cloud.shiftr.io

mqttjs connecting to an MQTT broker... +0ms
mqtt.js:10115 mqttjs:client MqttClient :: version: +0ms 5.4.0
mqtt.js:10115 mqttjs:client MqttClient :: environment +0ms browser
mqtt.js:10115 mqttjs:client MqttClient :: options.protocol +0ms wss
mqtt.js:10115 mqttjs:client MqttClient :: options.protocolVersion +0ms 4
mqtt.js:10115 mqttjs:client MqttClient :: options.username +0ms public
mqtt.js:10115 mqttjs:client MqttClient :: options.keepalive +0ms 60
mqtt.js:10115 mqttjs:client MqttClient :: options.reconnectPeriod +0ms 5000
mqtt.js:10115 mqttjs:client MqttClient :: options.rejectUnauthorized +0ms false
mqtt.js:10115 mqttjs:client MqttClient :: options.properties.topicAliasMaximum +0ms undefined
mqtt.js:10115 mqttjs:client MqttClient :: clientId +0ms qfuu3r
mqtt.js:10115 mqttjs:client MqttClient :: setting up stream +0ms
mqtt.js:10115 mqttjs:client connect :: calling method to clear reconnect +0ms
mqtt.js:10115 mqttjs:client _clearReconnect : clearing reconnect timer +0ms
mqtt.js:10115 mqttjs:client connect :: using streamBuilder provided to client to create stream +0ms
mqtt.js:10115 mqttjs calling streambuilder for +1ms wss
mqtt.js:10115 mqttjs:ws browserStreamBuilder +0ms
mqtt.js:10115 mqttjs:client connect :: pipe stream to writable stream +1ms
mqtt.js:10115 mqttjs:client connect: sending packet `connect` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: packet: Object +0ms
mqtt.js:10115 mqttjs:client _writePacket :: emitting `packetsend` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writing to stream +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writeToStream result true +15ms
examples/:1 Unchecked runtime.lastError: The message port closed before a response was received.
mqtt.js:10115 mqttjs:ws WebSocket onOpen +309ms
mqtt.js:10115 mqttjs:client writable stream :: parsing buffer +310ms
mqtt.js:10115 mqttjs:client parser :: on packet push to packets array. +1ms
mqtt.js:10115 mqttjs:client work :: getting next packet in queue +0ms
mqtt.js:10115 mqttjs:client work :: packet pulled from queue +0ms
mqtt.js:10115 mqttjs:client _handlePacket :: emitting packetreceive +0ms
mqtt.js:10115 mqttjs:client _handleConnack +0ms
mqtt.js:10115 mqttjs:client _setupPingTimer :: keepalive 60 (seconds) +0ms
mqtt.js:10115 mqttjs:client connect :: sending queued packets +2ms
mqtt.js:10115 mqttjs:client deliver :: entry undefined +0ms
mqtt.js:10115 mqttjs:client _resubscribe +0ms
mqtt.js:10115 mqttjs:client subscribe: array topic qfuu3r +0ms
mqtt.js:10115 mqttjs:client subscribe: pushing topic `qfuu3r` and qos `2` to subs list +0ms
mqtt.js:10115 mqttjs:client subscribe :: resubscribe true +0ms
mqtt.js:10115 mqttjs:client subscribe :: call _sendPacket +0ms
mqtt.js:10115 mqttjs:client _sendPacket :: (qfuu3r) ::  start +0ms
mqtt.js:10115 mqttjs:client _writePacket :: packet: Object +0ms
mqtt.js:10115 mqttjs:client _writePacket :: emitting `packetsend` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writing to stream +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writeToStream result true +1ms
mqtt.js:10115 mqttjs:client _writePacket :: invoking cb +0ms
mqtt.js:10115 mqttjs:client noop :: +0ms undefined
mqtt.js:10115 mqttjs:client subscribe: array topic topic +0ms
mqtt.js:10115 mqttjs:client subscribe: pushing topic `topic` and qos `0` to subs list +0ms
mqtt.js:10115 mqttjs:client subscribe :: resubscribe true +0ms
mqtt.js:10115 mqttjs:client subscribe :: call _sendPacket +0ms
mqtt.js:10115 mqttjs:client _sendPacket :: (qfuu3r) ::  start +0ms
mqtt.js:10115 mqttjs:client _writePacket :: packet: Object +0ms
mqtt.js:10115 mqttjs:client _writePacket :: emitting `packetsend` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writing to stream +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writeToStream result true +0ms
mqtt.js:10115 mqttjs:client _writePacket :: invoking cb +0ms
mqtt.js:10115 mqttjs:client noop :: +0ms undefined
mqtt.js:10115 mqttjs:client publish :: message `Hello from HTML` to topic `topic` +0ms
mqtt.js:10115 mqttjs:client publish :: qos +0ms 0
mqtt.js:10115 mqttjs:client MqttClient:publish: packet cmd: publish +0ms
mqtt.js:10115 mqttjs:client _sendPacket :: (qfuu3r) ::  start +0ms
mqtt.js:10115 mqttjs:client _writePacket :: packet: Object +0ms
mqtt.js:10115 mqttjs:client _writePacket :: emitting `packetsend` +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writing to stream +0ms
mqtt.js:10115 mqttjs:client _writePacket :: writeToStream result false +1ms
mqtt.js:10115 mqttjs:client _writePacket :: invoking cb +0ms
mqtt.js:10115 mqttjs:client noop :: +0ms undefined
mqtt.js:10115 mqttjs:client _sendPacket :: (qfuu3r) ::  end +0ms
mqtt.js:10115 mqttjs:client writable stream :: parsing buffer +25ms
3mqtt.js:10115 mqttjs:client parser :: on packet push to packets array. +0ms
mqtt.js:10115 mqttjs:client work :: getting next packet in queue +1ms
mqtt.js:10115 mqttjs:client work :: packet pulled from queue +0ms
mqtt.js:10115 mqttjs:client _handlePacket :: emitting packetreceive +0ms
mqtt.js:10115 mqttjs:client _handleAck :: packet type +0ms suback
mqtt.js:10115 mqttjs:client noop :: +0ms null
mqtt.js:10115 mqttjs:client work :: getting next packet in queue +0ms
mqtt.js:10115 mqttjs:client work :: packet pulled from queue +0ms
mqtt.js:10115 mqttjs:client _handlePacket :: emitting packetreceive +0ms
mqtt.js:10115 mqttjs:client _handleAck :: packet type +0ms suback
mqtt.js:10115 mqttjs:client noop :: +0ms null
mqtt.js:10115 mqttjs:client work :: getting next packet in queue +0ms
mqtt.js:10115 mqttjs:client work :: packet pulled from queue +0ms
mqtt.js:10115 mqttjs:client _handlePacket :: emitting packetreceive +0ms
mqtt.js:10115 mqttjs:client handlePublish: packet Packet {cmd: 'publish', retain: false, qos: 0, dup: false, length: 22, …} +0ms
mqtt.js:10115 mqttjs:client handlePublish: qos 0 +0ms

Solution

  • When using MQTT over WebSockets in the browser, the browser handles ALL the TLS work.

    The browser will also NOT prompt the user to accept an untrusted certificate for any connection made by the JavaScript code, unlike how it prompts when accessing a webpage.

    This means that the browser must trust any certificate that is used to protect a MQTT over WebSockets connection.

    This means you have 2 choices:

    1. Use a certificate from a Publically trusted CA (e.g. LetsEncrypt)
    2. You must import your CA certificate and trust it in to EVERY browser that will connect. This is only really practical for a single user system while developing, it will not work for any system that will be open to multiple users.