Search code examples
pythondockerdocker-composemqtt

MQTT user/password not recognized in docker env


I am playing with a MQTT docker-compose env and trying to use a username and password in the subscriber. Without the password both publish and subscribe work correctly.

My docker-compose.yml looks like this:

version: "3"
services:
  mqtt:
   image: eclipse-mosquitto:latest
   container_name: mqtt
   network_mode: bridge
   ports:
     - 1883:1883
   volumes:
     - ./conf:/mosquitto/config
     - ./log:/mosquitto/log   

My mosquitto.conf in /mosquitto/config/ on the container looks like this:

persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
allow_anonymous false
password_file /mosquitto/config/passwd.txt

My password file is in /mosquitto/config/passwd.txt and has a line with username and a clear password. I shelled into the container to the location of this file and did this

mosquitto_passwd -U /mosquitto/config/passwd.txt

which encrypts the file in place. When I look at the passwd.txt it has an entry with encrypted text:

user:$7$101$vOlxOuwvzHmJiYWC$N8MKHb4fczMZNPzCBfXK4k7mUbOp+PzwT2Yb4IeU1KMKABP8hOsvDjqe+DcK7q6ksVuGmdODfWjrjQNAfJMjZw==

When I run the publish it works fine. Along these lines:

import paho.mqtt.client as mqtt
import time

def on_publish(client,userdata,result):
   print("[PUB] Data published = %s" % result)

mqtt_broker ="localhost"
client = mqtt.Client("test")
client.on_publish = on_publish
client.connect(mqtt_broker, 1883)

for i in range(10):
   temp = i
   client.publish("Temp", temp)
   print("[PUB] Just published %.4f to topic Temp" % temp)
   time.sleep(1)

The subscribe however never gets it:

import paho.mqtt.client as mqtt
username='user'
password='passwd'

def on_message(client, userdata, message):
   print("[SUB] received message: %s" % message.payload.decode("utf-8"))

def on_connect(client, userdata, flags, rc):
   print("[SUB] Connected with result code "+str(rc))
   client.subscribe("/Temp")

mqtt_broker = "localhost"

client = mqtt.Client("sub")
ttl = 120
client.username_pw_set(username, password)
client.connect(mqtt_broker, 1883, ttl)

client.loop_start()

client.subscribe("Temp")
print("[SUB] Awaiting notifications")
client.loop_forever()

In the logs I see not authorised errors:

1670429277: New connection from 172.17.0.1:59538 on port 1883.
1670429277: Client mac disconnected, not authorised.

What could be the issue?


Solution

  • As per the comments the issue in this case was that your "publish" did not set the username and password (but you do in your subscribe code). It's worth using a known good program (e.g. mosquitto_sub/mosquitto_pub) when testing (otherwise you might have bugs in both your subscribe and publish code which makes debugging confusing!). I'll leave the rest of my answer in case it helps anyone else.

    I have been able to run your code successfully after making a few tweaks. This may not be a full answer because I have not been able to replicate your issue, but can point you towards code that works.

    The main issue I can see in your code is that you are not configuring client to use your callbacks. e.g.:

    client.on_message = on_message
    client.on_connect = on_connect
    

    This means that your program will not subscribe (and would do nothing with any messages received).

    In addition to the above you are using both the threaded and blocking loop functions. This will cause issues because you end up running multiple instances of the network loop. I fixed these issues and made a few other small changes:

    import paho.mqtt.client as mqtt
    import time
    
    username='user'
    password='passwd'
    
    def on_message(client, userdata, message):
        print("[SUB] received message: %s" % message.payload.decode("utf-8"))
    
    def on_connect(client, userdata, flags, rc):
        print("[SUB] Connected with result code "+str(rc))
        client.subscribe("/Temp")
    
    time.sleep(5)# Allow time for mosquitto to startup
    
    mqtt_broker = "mqtt"
    
    client = mqtt.Client("sub")
    client.on_message = on_message
    client.on_connect = on_connect
    ttl = 120
    client.username_pw_set(username, password)
    client.connect(mqtt_broker, 1883, ttl)
    
    #client.loop_start() # Pick either threaded or non-threaded, not both!
    
    client.subscribe("Temp")
    print("[SUB] Awaiting notifications")
    client.loop_forever()
    

    I ran your code and mosquitto using docker - config follows (happy to add Dockerfile etc if needed):

    version: '3.4'
    
    services:
      mqtt:
        image: eclipse-mosquitto:latest
        ports:
          - "1883:1883"
        volumes:
          - ./conf:/mosquitto/config
          - ./log:/mosquitto/log
      pythondocker:
        image: pythondocker
        build:
          context: .
          dockerfile: ./Dockerfile
    

    With this running I used mosquitto_pub.exe -h 127.0.0.1 -u user -P passwd -t Temp -m "foo" on the host to send a message which was successfully received.

    Attaching to pythondocker-mqtt-1, pythondocker-pythondocker-1
    pythondocker-pythondocker-1  | [SUB] Awaiting notifications
    pythondocker-pythondocker-1  | [SUB] Connected with result code 0
    pythondocker-pythondocker-1  | [SUB] received message: foo
    

    If I modify the python, changing the pasword to passwd2 then I do get the error you were seeing (not sure why you are getting this - I am using the passwd.txt you provided):

    1670453773: New connection from 172.26.0.2:40037 on port 1883.
    1670453773: Client sub disconnected, not authorised.