171 lines
8.1 KiB
Markdown
Raw Permalink Normal View History

2019-06-12 12:28:07 +02:00
# Docker Image containing the Mosquitto MQTT Broker and the mosquitto-auth-plug
2019-06-13 12:47:38 +02:00
This project includes the mosquitto MQTT broker (https://github.com/eclipse/mosquitto, see also https://mosquitto.org/) and the mosquitto-go-auth (https://github.com/iegomez/mosquitto-go-auth forked into https://github.com/wollud1969/mosquitto-go-auth) as submodules.
2019-06-12 12:28:07 +02:00
2022-09-14 14:19:01 +02:00
It additionally includes the Let's Encrypt `certbot` and some mimic for automatic renewal of certificates using `supervisord` and `cron`.
2019-06-12 12:28:07 +02:00
Using Gitlab CI and a Dockerfile included in this project a Docker image based on Debian Linux is created.
## Mosquitto MQTT Broker
The Mosquitto MQTT Broker in this Docker image is built beyond the default build configuration with websockets support.
2019-06-13 12:49:14 +02:00
## mosquitto-go-auth
2019-06-12 12:28:07 +02:00
2019-06-13 12:49:14 +02:00
The mosquitto-go-auth supports a couple of backends and it seems that all backends will be built anyway. For me so far only the MariaDB/MySQL backend is in use and thus tested and documented here.
2019-06-12 12:28:07 +02:00
## Running the container
2022-09-14 14:19:01 +02:00
You can not run a container based on this image "out-of-the-box". You need to edit the configuration, and if desired, run all the Let's Encrypt stuff. For details see below.
2019-06-12 12:28:07 +02:00
The container exposed the ports 1883 (MQTT), 8883 (MQTT over SSL) and 9001 (MQTT over websockets). Only the configuration directory containing `mosquitto.conf` and friends is prepared as a volume.
2022-09-14 14:19:01 +02:00
Besides the mosquitto configuration volume, there are volume required for the Let's Encrypt configuration and state, the data directory of the broker and for the logfiles for `supervisord`.
Due to the requirements of `certbot` it also exposed the port 80 and 443. So, be careful when trying to start this image as a container on the same host as a webserver.
All logging is send into a dedicated logfile under control of `supervisord`.
2019-06-12 12:28:07 +02:00
To start the container a script is provided, which might need to adjusted to the actual environment:
2019-06-13 17:21:40 +02:00
2019-06-12 12:28:07 +02:00
#!/bin/bash
2022-09-14 14:19:01 +02:00
IMAGE=wollud1969/mosquitto-with-auth:latest
2019-06-13 17:21:40 +02:00
VOLUME_CONFIG=mosquitto-config
VOLUME_DATA=mosquitto-data
2022-09-14 14:19:01 +02:00
VOLUME_LOG=mosquitto-log
VOLUME_LETSENCRYPT=mosquitto-letsencrypt
2019-06-12 12:28:07 +02:00
2019-06-13 17:21:40 +02:00
docker volume inspect $VOLUME_CONFIG > /dev/null || docker volume create $VOLUME_CONFIG
docker volume inspect $VOLUME_DATA > /dev/null || docker volume create $VOLUME_DATA
2022-09-14 14:19:01 +02:00
docker volume inspect $VOLUME_LOG > /dev/null || docker volume create $VOLUME_LOG
docker volume inspect $VOLUME_LETSENCRYPT > /dev/null || docker volume create $VOLUME_LETSENCRYPT
2019-06-12 12:28:07 +02:00
docker pull $IMAGE
docker run \
2022-09-14 14:19:01 +02:00
-d \
--rm \
-p80:80 \
-p443:443 \
-p1883:1883 \
-p8883:8883 \
-p9001:9001 \
-v $VOLUME_CONFIG:/opt/etc/mosquitto \
-v $VOLUME_DATA:/opt/data \
-v $VOLUME_LOG:/var/log/supervisor \
-v $VOLUME_LETSENCRYPT:/etc/letsencrypt \
--name mosquitto \
$IMAGE
2019-06-13 17:21:40 +02:00
2019-06-12 12:28:07 +02:00
2022-09-14 14:19:01 +02:00
The container expects the main configuration file in the root of the configuration volume named `mosquitto.conf`.
2019-06-12 12:28:07 +02:00
2022-09-14 14:19:01 +02:00
A very simple configuration, supporting MQTT on port 1883 and over TLS on port 8883 is:
2019-06-12 12:28:07 +02:00
2019-06-13 17:21:40 +02:00
2019-06-12 12:28:07 +02:00
log_dest stdout
2019-06-13 17:21:40 +02:00
persistence true
persistence_location /opt/data/
2019-06-12 12:28:07 +02:00
listener 1883
protocol mqtt
#allow_anonymous true
allow_anonymous false
2022-09-14 14:19:01 +02:00
listener 8883
protocol mqtt
#allow_anonymous true
allow_anonymous false
certfile /opt/etc/mosquitto/server.crt
keyfile /opt/etc/mosquitto/server.key
dhparamfile /opt/etc/mosquitto/dh.pem
tls_version tlsv1.2
2019-06-13 12:47:38 +02:00
auth_plugin /opt/lib/go-auth.so
auth_opt_log_dest stdout
auth_opt_log_level debug
2019-06-12 12:28:07 +02:00
auth_opt_backends mysql
2019-06-13 12:47:38 +02:00
auth_opt_mysql_host mariadb
auth_opt_mysql_port 3306
auth_opt_mysql_dbname mosquittoauth
auth_opt_mysql_user mosquittoauth
2019-06-13 12:52:54 +02:00
auth_opt_mysql_password xxx
2019-06-13 12:47:38 +02:00
auth_opt_mysql_allow_native_passwords true
auth_opt_mysql_userquery SELECT pw FROM users WHERE username = ?
auth_opt_mysql_aclquery SELECT topic FROM acls WHERE username = ? AND (rw & ?) != 0
2019-06-13 17:21:40 +02:00
2019-06-13 12:47:38 +02:00
The original readme of the mosquitto-go-auth plugin proposes a different acl query. However, that one didn't work for me.
Maybe the meaning of the access attribute handed over from mosquitto core to the plugin has been changed in between.
Actually, it appears to me that the meaning of this attribute has to be interpreted bitwise: Bit0 (1) is read access, Bit1 (2) is write access (publish), Bit0 and Bit1 (3) is readwrite access and Bit2 (4) is subscribe access. Write access is obviously and verified be test publish and subscribe access is also obviously subscribe. Currently I don't know what is meant be read access. For this reason I'm using a bitwise operation in the acl query. I set the rw column for those users who should have read-only access to 5 (1&4), for users who should only publish to 2 and for those ones who should read and write to 7 (1&2&4).
2019-06-12 12:28:07 +02:00
The required schema in the database is
2019-10-10 09:40:24 +00:00
CREATE TABLE users_t (
id INTEGER AUTO_INCREMENT,
username VARCHAR(25) NOT NULL,
pw VARCHAR(512) NOT NULL,
super INT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX users_username ON users_t (username);
CREATE OR REPLACE VIEW users AS
SELECT username, pw, super
FROM users_t;
CREATE TABLE acls_t (
id INTEGER AUTO_INCREMENT,
user INTEGER NOT NULL,
topic VARCHAR(256) NOT NULL,
rw INTEGER(1) NOT NULL DEFAULT 1, -- 1 is read, 2 is write, 3 is readwrite, 4 is subscribe
PRIMARY KEY (id),
2023-02-07 12:17:46 +01:00
CONSTRAINT `fk_users_user`
2019-10-10 09:40:24 +00:00
FOREIGN KEY (user) REFERENCES users_t (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE UNIQUE INDEX acls_user_topic ON acls_t (user, topic);
CREATE OR REPLACE VIEW acls AS
SELECT a.topic, a.rw,
u.username
FROM users_t u, acls_t a
WHERE a.user = u.id;
2019-06-13 12:47:38 +02:00
The password is generated using the `pw` tool provided by mosquitto-go-auth, which is included in the image at `/opt/bin`. It can be used either within the container using `docker exec -it <mosquitto-container> /opt/bin/pw`. You may also try to copy it from the container onto your Linux host. It should run, since it is only linked against typical Linux libraries, however, I wouldn't do that.
2019-06-12 12:28:07 +02:00
2019-06-13 12:47:38 +02:00
For further information consult the readme and the examples in the mosquitto-go-auth project (https://github.com/iegomez/mosquitto-go-auth or https://github.com/wollud1969/mosquitto-go-auth).
2019-06-12 12:28:07 +02:00
2023-02-07 12:17:46 +01:00
For MariaDB and PostgreSQL there are prepared table create statements in the repository,
For PostgresSQL there is a prepared Python tool in the directory `tools` available to added users into the database.
2019-06-12 12:28:07 +02:00
2022-09-14 14:19:01 +02:00
## Preparing configuration and certificates
* Start the container using the provided start script, follow the container log using `docker logs -f <containername>`, you will see that `supervisord` start `cron` and `mosquitto` and you will see that the start of `mosquitto` fails
* Go into the container using `docker exec -it <containername> bash`
* Go into the directory `/opt/etc/mosquitto`, copy `mosquitto.conf-sample` into `mosquitto.conf` and edit it if required
If you want to register at Let's Encrypt and obtain a certificate follow the next steps:
* Generate Diffie-Hellman parameters in the broker's configuration directory using `openssl dhparam -out /opt/etc/mosquitto/dh.pem 2048`
* Register at Let's Encrypt using `certbot register`
* Obtain a certificate using `certbot certonly -d <domainname> --standalone`, make sure to add the domainname into DNS first
* Copy the deployment script into the deploy hooks directory of Let's Encrypt: `cp /opt/bin/cert-deploy.sh /etc/letsencrypt/renewal-hooks/deploy/`, edit it to fill in the right domainname
* Run the deployment script manually for the very first deployment of certificates: `env RENEWED_DOMAINS=<domainname> RENEWED_LINEAGE=/etc/letsencrypt/live/<domainname> ./cert-deploy.sh`
* The certificate and private key is now copied from the Let's Encrypt state directory into the configuration directory of `mosquitto` and the broker is restarted, you can observe that in the container logging output
* Finally, test the broker using something like `mosquitto_sub -h <domainname> -p 8883 --tls-version tlsv1.2 -v -t test` and `mosquitto_pub -h <domainname> -p 8883 --tls-version tlsv1.2 -t test -m test123`
* Renewal of the certificate will be triggered once a week
2019-06-12 12:28:07 +02:00