Forward Compatibility for Mosquitto MQTT Broker with Docker Compose v2
Tags: docker, mqtt
Published: 2023-10-23 20:36:08

# Planning

While working on a personal project [Komponist][1], I was due to update 
[Mosquitto MQTT Broker][2] due to a [CVE][3] 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__:

```bash
|- docker-compose.mosquitto.yml
|-- mosquitto
    |--- acl
    |--- users
    |--- mosquitto.conf
```

The Compose file content is:

```yaml
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:

```bash
docker compose -f docker-compose.mosquitto.yml up
```

The logs will throw some warnings such as the following:

```bash
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:

1. change the user for the core configuration files
2. change the file permissions for the core configuration files
3. 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:

```yaml
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 the `docker-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:

```yaml
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:

```bash
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](https://gist.github.com/shantanoo-desai/1634f3206ce486707a4c047bcc043c4c)

[1]: https://github.com/shantanoo-desai/komponist
[2]: https://github.com/eclipse/mosquitto
[3]: https://github.com/eclipse/mosquitto/blob/v2.0.18/ChangeLog.txt#L27