Forward Compatibility for Mosquitto MQTT Broker with Docker Compose v2
Planning
While working on a personal project Komponist, it was due to update Mosquitto MQTT Broker due to a CVE and found some interesting changes that will impact me in the future when it comes to configuring the Broker. This post provides a solution to make the Broker compatible with future versions using new and less visited concepts in Docker Compose v2, namely:
- Docker Init Containers
- Using the
include
feature in Docker Compose files
Observations
This section provides information on what currently is observed post upgrading the Mosquitto Broker to v2.0.18.
Current Docker Compose File
This is my current docker-compose.mosquitto.yml
with the following file structure
generated by Komponist:
|- docker-compose.mosquitto.yml
|-- mosquitto
|--- acl
|--- users
|--- mosquitto.conf
The Compose file content is:
services:
mosquitto:
image: docker.io/eclipse-mosquitto:2.0.18
container_name: komponist_mosquitto
configs:
- mosquitto_conf
- mosquitto_acl
- mosquitto_users
command: mosquitto -c /mosquitto_conf
security_opt:
- "no-new-privileges=true"
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
configs:
mosquitto_conf:
file: ./mosquitto/mosquitto.conf
mosquitto_acl:
file: ./mosquitto/acl
mosquitto_users:
file: ./mosquitto/users
Bringing the container up using:
docker compose -f docker-compose.mosquitto.yml up
The logs will throw some warnings such as the following:
Warning: File /mosquitto_users has world readable permissions. Future versions will refuse to load this file.
Warning: File /mosquitto_users owner is not mosquitto. Future versions will refuse to load this file.
...
...
So for future versions the files need to have a specific user and file permissions to make the current configuration files valid.
Caveats
In systems where I do not have superuser privileges to create a new user mosquitto
whose
UID/GID should be 1883
and changing file permissions is not an option, the only way to tackle
such a solution is through Docker Init Containers.
Docker Init Containers are like sidecar containers in Kubernetes or a patching container where it initializes a state (here specific configuration file) and then starts the core container.
This feature has existed for a while, but it is not well documented in the Compose Specifications
with some practical examples. In essence, we will use the depends_on
service dependency logic
between a generic alpine
container that will:
- change the user for the core configuration files
- change the file permissions for the core configuration files
- pass on the core files to the
eclipse-mosquitto
broker using shared volumes
Once this container is mounted with the files and is run successfully, the adapted configuration files will be passed on to the main docker container for the Mosquitto MQTT Broker.
Solution
We keep the file structure same as previously mentioned but we create a new Compose file
called docker-compose.mosquitto-init.yml
file as follows:
services:
mosquitto_init:
image: alpine:3.18
container_name: mosquitto_init
command: sh -c 'chmod 0400 -R /config/ && chown 1883:1883 -R /config/'
volumes:
- ./mosquitto/mosquitto.conf:/config/mosquitto_conf
- ./mosquitto/users:/config/mosquitto_users
- ./mosquitto/acl:/config/mosquitto_acl
We are mounting the configuration files into the init container under the /config
directory
and changing the file persmissions and ownership for the files in this init container.
NOTE: we will have to refactor out the Docker Configs section previously configured in original
docker-compose.mosquitto.yml
file into thedocker-compose.mosquitto-init.yml
file
We now refactor the mosquitto.conf
file a bit to accept the new location for the files
as follows:
# MQTT Port Listener
listener 1883
protocol mqtt
# Authentication
allow_anonymous false
password_file /config/mosquitto_users
# Authorization
acl_file /config/mosquitto_acl
# Logging Configuration
log_timestamp true
log_type all
Note the /config
as prefix to the ACL and password file.
Final refactoring takes place in the docker-compose.mosquitto.yml
file as follows:
include:
- docker-compose.mosquitto-init.yml
services:
mosquitto:
image: docker.io/eclipse-mosquitto:2.0.18
container_name: komponist_mosquitto
entrypoint: mosquitto -c /config/mosquitto_conf
depends_on:
mosquitto_init:
condition: service_completed_successfully
security_opt:
- "no-new-privileges=true"
volumes_from:
- mosquitto_init
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
Upon bringing the stack up again:
docker compose -f docker-compose.mosquitto.yml up
the Warnings should be gone and your config files that were valid prior to version 2.0.18 will still be compatible for the upcoming versions (unless the development of mosquitto demands something more in the future).
In a Nutshell, the main mosquitto
service is now dependent on the mosquitto-init
service
via the depends_on
spec and core files are exchanged from the init container to the main container
via volumes_from
spec.
NOTE: this approach will change the file permissions / owner for the core configuration files on the host machine since they are volume mounted.
At the time of writing this post, this is the only approach I have tested out.
If you have read this post and have better approaches in mind, connect with me on LinkedIn and share your solutions or improve upon the method. I would love to hear your feedback.
GitHub Gist
the Gist of the code can be found here