Skip to main content

INSTAR MQTT with RabbitMQ

Guangzhou, China

Installing RabbitMQ with MQTT and MQTT over Websocket support using Docker. Using the TLS Generator to generate a valid CA certificate for the TLS encryption.

Docker Compose Build

Start by creating the following environment files that will set the RabbitMQ Image version and the default user that can be used to login to the RabbitMQ Management Interface as well as the MQTT broker.

RabbitMQ Configuration

config/login.env

RABBITMQ_DEFAULT_USER=admin
RABBITMQ_DEFAULT_PASS=instar

MQTT Broker Configuration

The configuration below is part of the default config/rabbitmq.conf - the latest example configuration file can be found here:

# =======================================
# MQTT section
# =======================================

## TCP listener settings.
##
# mqtt.listeners.tcp.1 = 127.0.0.1:61613
# mqtt.listeners.tcp.2 = ::1:61613
mqtt.listeners.tcp.1 = 0.0.0.0:1883

## TCP listener options (as per the broker configuration).
##
mqtt.tcp_listen_options.backlog = 4096
mqtt.tcp_listen_options.recbuf = 131072
mqtt.tcp_listen_options.sndbuf = 131072
#
mqtt.tcp_listen_options.keepalive = true
mqtt.tcp_listen_options.nodelay = true
#
mqtt.tcp_listen_options.exit_on_close = true
mqtt.tcp_listen_options.send_timeout = 120000

## TLS listener settings
## ## See https://rabbitmq.com/mqtt.html and https://rabbitmq.com/ssl.html for details.
#
# mqtt.listeners.ssl.default = 8883
#
# ssl_options.cacertfile = /path/to/tls/ca_certificate_bundle.pem
# ssl_options.certfile = /path/to/tls/server_certificate.pem
# ssl_options.keyfile = /path/to/tls/server_key.pem
# ssl_options.verify = verify_peer
# ssl_options.fail_if_no_peer_cert = true
#


## Number of Erlang processes that will accept connections for the TCP
## and TLS listeners.
##
mqtt.num_acceptors.tcp = 10
mqtt.num_acceptors.ssl = 10

## Whether or not to enable proxy protocol support.
## Once enabled, clients cannot directly connect to the broker
## anymore. They must connect through a load balancer that sends the
## proxy protocol header to the broker at connection time.
## This setting applies only to STOMP clients, other protocols
## like STOMP or AMQP have their own setting to enable proxy protocol.
## See the plugins or broker documentation for more information.
##
# mqtt.proxy_protocol = false

## Set the default user name and password used for anonymous connections (when client
## provides no credentials). Anonymous connections are highly discouraged!
##
mqtt.default_user = admin
mqtt.default_pass = instar

## Enable anonymous connections. If this is set to false, clients MUST provide
## credentials in order to connect. See also the mqtt.default_user/mqtt.default_pass
## keys. Anonymous connections are highly discouraged!
##
mqtt.allow_anonymous = false

## If you have multiple vhosts, specify the one to which the
## adapter connects.
##
# mqtt.vhost = /

## Specify the exchange to which messages from MQTT clients are published.
##
# mqtt.exchange = amq.topic

## Specify TTL (time to live) to control the lifetime of non-clean sessions.
##
mqtt.subscription_ttl = 1800000

## Set the prefetch count (governing the maximum number of unacknowledged
## messages that will be delivered).
##
mqtt.prefetch = 10
##
## Sets the durable queue type to be used for QoS 1 subscriptions.
##
## Supported types are:
##
## * classic
## * quorum
##
## IMPORTANT: changing this setting requires all existing queues used by
## the MQTT plugin to be DELETED or clients will fail to subscribe.
## So this setting should be used for new clusters.
##
mqtt.durable_queue_type = classic
#
# =======================================
# MQTT over WS section
# =======================================
web_mqtt.tcp.port = 9001
# web_mqtt.ssl.port = 15676
# web_mqtt.ssl.backlog = 1024
# web_mqtt.ssl.cacertfile = /path/to/ca_certificate.pem
# web_mqtt.ssl.certfile = /path/to/server_certificate.pem
# web_mqtt.ssl.keyfile = /path/to/server_key.pem
# needed when private key has a passphrase
# web_mqtt.ssl.password = changeme
# web_mqtt.ssl.port = 15676
# web_mqtt.ssl.backlog = 1024
# web_mqtt.ssl.certfile = /path/to/server_certificate.pem
# web_mqtt.ssl.keyfile = /path/to/server_key.pem
# web_mqtt.ssl.cacertfile = /path/to/ca_certificate_bundle.pem
# web_mqtt.ssl.password = changeme

# web_mqtt.ssl.honor_cipher_order = true
# web_mqtt.ssl.honor_ecc_order = true
# web_mqtt.ssl.client_renegotiation = false
# web_mqtt.ssl.secure_renegotiate = true

# web_mqtt.ssl.versions.1 = tlsv1.2
# web_mqtt.ssl.versions.2 = tlsv1.1
# web_mqtt.ssl.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
# web_mqtt.ssl.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
# web_mqtt.ssl.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
# web_mqtt.ssl.ciphers.4 = ECDHE-RSA-AES256-SHA384
# web_mqtt.ssl.ciphers.5 = ECDH-ECDSA-AES256-GCM-SHA384
# web_mqtt.ssl.ciphers.6 = ECDH-RSA-AES256-GCM-SHA384
# web_mqtt.ssl.ciphers.7 = ECDH-ECDSA-AES256-SHA384
# web_mqtt.ssl.ciphers.8 = ECDH-RSA-AES256-SHA384
# web_mqtt.ssl.ciphers.9 = DHE-RSA-AES256-GCM-SHA384
# web_mqtt.proxy_protocol = true
#
## Cowboy HTTP
# connection inactivity timeout
web_mqtt.cowboy_opts.idle_timeout = 60000
# max number of pending requests allowed on a connection
web_mqtt.cowboy_opts.max_keepalive = 200
# max number of headers in a request
web_mqtt.cowboy_opts.max_headers = 100
# max number of empty lines before request body
web_mqtt.cowboy_opts.max_empty_lines = 5
# max request line length allowed in requests
web_mqtt.cowboy_opts.max_request_line_length = 8000

# WebSocket traffic compression is enabled by default
web_mqtt.ws_opts.compress = true
# WebSocket connection inactivity timeout
web_mqtt.ws_opts.idle_timeout = 60000
web_mqtt.ws_opts.max_frame_size = 50000

Build

docker-compose.yml

version: "3.8"
services:
rabbitmq:
image: rabbitmq:management-alpine
container_name: rabbitmq
restart: unless-stopped
build:
context: docker/
ports:
- "1883:1883"
- "5672:5672"
- "8080:15672"
- "1885:1885"
command: "/bin/bash -c \"rabbitmq-plugins enable --offline rabbitmq_mqtt rabbitmq_web_mqtt rabbitmq_amqp1_0; rabbitmq-server\""
env_file:
- config/login.env
volumes:
- type: bind
source: ./config/rabbitmq.conf
target: /etc/rabbitmq/rabbitmq.conf
read_only: true
- type: bind
source: ./config/advanced.config
target: /etc/rabbitmq/advanced.config
read_only: true
networks:
brokernet:
ipv4_address: 172.16.238.101

networks:
brokernet:
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"
ipam:
driver: default
config:
- subnet: 172.16.238.0/24

First run the build command docker-compose build the prepare the Docker image based on the rabbitmq/Dockerfile:

FROM rabbitmq:management-alpine
RUN rabbitmq-plugins enable --offline rabbitmq_mqtt rabbitmq_web_mqtt rabbitmq_amqp1_0

Adding Encryption

Generate the certificates:

git clone https://github.com/mpolinowski/tls-gen
cd tls-gen/basic
make PASSWORD=<your_password_here>
make verify
make info
ls -l ./resultcd result
openssl rsa -in client_key.pem -out client_key_unencrypted.pem
ca_certificate.pem
ca_key.pem
client_certificate.pem
client_key.p12
client_key.pem
client_key_unencrypted.pem
server_certificate.pem
server_key.p12
server_key.pem

Add them to rabbitmq.conf:

listeners.ssl.default = 5671
mqtt.listeners.tcp = none
mqtt.listeners.ssl.default = 8883ssl_options.cacertfile = /etc/rabbitmq/cert/ca_certificate.pem
ssl_options.certfile = /etc/rabbitmq/cert/server_certificate.pem
ssl_options.keyfile = /etc/rabbitmq/cert/server_key.pem
ssl_options.password = <your_password_here>
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
ssl_options.versions.1 = tlsv1.3
ssl_options.versions.2 = tlsv1.2
ssl_options.versions.3 = tlsv1.1

And mount the cert folder into your container Dockerfile:

COPY ./cert/ /etc/rabbitmq/cert
RUN chown -R rabbitmq:rabbitmq /etc/rabbitmq/cert

config/advanced.config

[


%% ----------------------------------------------------------------------------
%% Advanced Erlang Networking/Clustering Options.
%%
%% See https://www.rabbitmq.com/clustering.html for details
%% ----------------------------------------------------------------------------
%% Sets the net_kernel tick time.
%% Please see http://erlang.org/doc/man/kernel_app.html and
%% https://www.rabbitmq.com/nettick.html for further details.
%%
%% {kernel, [{net_ticktime, 60}]},
%% ----------------------------------------------------------------------------
%% RabbitMQ Shovel Plugin
%%
%% See https://www.rabbitmq.com/shovel.html for details
%% ----------------------------------------------------------------------------

{rabbitmq_shovel,
[{shovels,
[%% A named shovel worker.
%% {my_first_shovel,
%% [

%% List the source broker(s) from which to consume.
%%
%% {sources,
%% [%% URI(s) and pre-declarations for all source broker(s).
%% {brokers, ["amqp://user:password@host.domain/my_vhost"]},
%% {declarations, []}
%% ]},

%% List the destination broker(s) to publish to.
%% {destinations,
%% [%% A singular version of the 'brokers' element.
%% {broker, "amqp://"},
%% {declarations, []}
%% ]},

%% Name of the queue to shovel messages from.
%%
%% {queue, <<"your-queue-name-goes-here">>},

%% Optional prefetch count.
%%
%% {prefetch_count, 10},

%% when to acknowledge messages:
%% - no_ack: never (auto)
%% - on_publish: after each message is republished
%% - on_confirm: when the destination broker confirms receipt
%%
%% {ack_mode, on_confirm},

%% Overwrite fields of the outbound basic.publish.
%%
%% {publish_fields, [{exchange, <<"my_exchange">>},
%% {routing_key, <<"from_shovel">>}]},

%% Static list of basic.properties to set on re-publication.
%%
%% {publish_properties, [{delivery_mode, 2}]},

%% The number of seconds to wait before attempting to
%% reconnect in the event of a connection failure.
%%
%% {reconnect_delay, 2.5}

%% ]} %% End of my_first_shovel
]}
%% Rather than specifying some values per-shovel, you can specify
%% them for all shovels here.
%%
%% {defaults, [{prefetch_count, 0},
%% {ack_mode, on_confirm},
%% {publish_fields, []},
%% {publish_properties, [{delivery_mode, 2}]},
%% {reconnect_delay, 2.5}]}
]},

{rabbitmq_auth_backend_ldap, [
%%
%% Authorisation
%% =============
%%

%% The LDAP plugin can perform a variety of queries against your
%% LDAP server to determine questions of authorization. See
%% https://www.rabbitmq.com/ldap.html#authorization for more
%% information.

%% Set the query to use when determining vhost access
%%
%% {vhost_access_query, {in_group,
%% "ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}},

%% Set the query to use when determining resource (e.g., queue) access
%%
%% {resource_access_query, {constant, true}},

%% Set queries to determine which tags a user has
%%
%% {tag_queries, []}
]}
].

Start the Container

docker-compose up

And login with the default login defined above:

INSTAR MQTT with RabbitMQ

I recommend connecting the MQTT Explorer for debugging:

INSTAR MQTT with RabbitMQ

INSTAR MQTT with RabbitMQ

Once the MQTT Explorer is connected copy the configuration over to your INSTAR IP camera and click to connect:

INSTAR MQTT with RabbitMQ

Wait a few seconds for your camera state objects to be synced:

INSTAR MQTT with RabbitMQ