DEBOPS.MOSQUITTO(5)

NAME
debops.mosquitto - Manage a Mosquitto MQTT broker using Ansible

SYNOPSIS

debops service/mosquitto [--limit group,host,...] [--diff] [--check] [--tags tag1,tag2,...] [--skip-tags tag1,tag2,...] [<ansible-playbook options>] ...

DESCRIPTION

Mosquitto <https://mosquitto.org/> is an open source Message Queue Telemetry Transport (MQTT) <https://en.wikipedia.org/wiki/MQTT> broker used in the Internet of Things <https://en.wikipedia.org/wiki/Internet_of_things> paradigm.

The debops.mosquitto Ansible role can be used to install and configure Mosquitto on Debian/Ubuntu hosts. The role can use other DebOps roles to manage firewall access to Mosquitto, publish Avahi services and configure an nginx frontend for the Mosquitto WebSockets API.

GETTING STARTED

  Default configuration

With the default configuration, the debops.mosquitto role will configure Mosquitto with a TLS listener accepting connections from any host (if the debops.pki environment is detected), and plaintext listener accepting connections only from localhost. Anonymous access to the broker will be allowed.

By default, plaintext connections are not allowed over the network. You can change that by setting the mosquitto__allow variable with a list of IP addresses or CIDR subnets to allow plaintext connections.

  WebSocket configuration

WebSocket support will be enabled if a sufficient version of the libwebsockets library is detected; this was tested and works on Debian Stretch; using upstream Mosquitto on older OS releases is not enough, and at the time the role was written upstream Stretch version was not available. The WebSocket listener will be configured for localhost connections only.

The role provides two playbooks, a "plain" and "nginx" version (see below). The "nginx" playbook can be used to configure an nginx reverse proxy for the WebSocket listener. By default any host will be able to access the WebSocket connection, this can be controlled separately from the normal TLS connection using mosquitto__websockets_allow variable.

You can test support for WebSockets in Mosquitto using: http://www.hivemq.com/blog/full-featured-mqtt-client-browser

  User accounts, ACL, password creation

For better security, you should configure some user accounts (see mosquitto__auth_users) and ACL entries for the broker. When user accounts are defined, the role will automatically block the anonymous access; this can be controlled using the mosquitto__allow_anonymous variable.

The older Mosquitto releases (pre 1.4+) provide a mosquitto_passwd command which does not support batch password creation, therefore the role will not configure the user accounts if such version is detected. You should either enable upstream release (on older OS releases) or use Debian Stretch+ which supports batch password creation.

  Avahi CNAME (alias) support

If the Avahi support managed by the debops.avahi Ansible role is detected, the debops.mosquitto role will automatically create Avahi service entries for the configured listeners. The Avahi configuration is managed by the avahi_* parameters in the listener configuration, see mosquitto__listeners for more details.

  Example inventory

The debops.mosquitto role needs to be enabled to be used in the DebOps playbook. To do that, add the hosts that you want to configure Avahi on to the [debops_service_mosquitto] Ansible inventory group:

[debops_service_mosquitto]
hostname

If you want to enable the nginx reverse proxy on a given host for WebSocket support, you can add that host to the [debops_service_mosquitto_nginx] Ansible inventory group:

[debops_service_mosquitto_nginx]
hostname

  Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.mosquitto role:

---
# Copyright (C) 2017 Maciej Delmanowski <drybjed@gmail.com>
# Copyright (C) 2017 DebOps <https://debops.org/>
# SPDX-License-Identifier: GPL-3.0-only
- name: Configure Mosquitto service
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_mosquitto' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  roles:
    - role: keyring
      tags: [ 'role::keyring', 'skip::keyring', 'role::mosquitto' ]
      keyring__dependent_apt_keys:
        - '{{ mosquitto__keyring__dependent_apt_keys }}'
    - role: etc_services
      tags: [ 'role::etc_services', 'skip::etc_services' ]
      etc_services__dependent_list:
        - '{{ mosquitto__etc_services__dependent_list }}'
    - role: tcpwrappers
      tags: [ 'role::tcpwrappers', 'skip::tcpwrappers' ]
      tcpwrappers__dependent_allow:
        - '{{ mosquitto__tcpwrappers__dependent_allow }}'
    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
      ferm__dependent_rules:
        - '{{ mosquitto__ferm__dependent_rules }}'
    - role: python
      tags: [ 'role::python', 'skip::python', 'role::mosquitto' ]
      python__dependent_packages3:
        - '{{ mosquitto__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ mosquitto__python__dependent_packages2 }}'
    - role: mosquitto
      tags: [ 'role::mosquitto', 'skip::mosquitto' ]

There is a separate playbook for a Mosquitto instance with nginx used as a reverse proxy for WebSocket connections:

---
- name: Configure Mosquitto service with Nginx
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_mosquitto_nginx' ]
  become: True
  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'
  roles:
    - role: keyring
      tags: [ 'role::keyring', 'skip::keyring', 'role::nginx', 'role::mosquitto' ]
      keyring__dependent_apt_keys:
        - '{{ nginx__keyring__dependent_apt_keys }}'
        - '{{ mosquitto__keyring__dependent_apt_keys }}'
    - role: apt_preferences
      tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
      apt_preferences__dependent_list:
        - '{{ nginx__apt_preferences__dependent_list }}'
    - role: etc_services
      tags: [ 'role::etc_services', 'skip::etc_services' ]
      etc_services__dependent_list:
        - '{{ mosquitto__etc_services__dependent_list }}'
    - role: tcpwrappers
      tags: [ 'role::tcpwrappers', 'skip::tcpwrappers' ]
      tcpwrappers__dependent_allow:
        - '{{ mosquitto__tcpwrappers__dependent_allow }}'
    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
      ferm__dependent_rules:
        - '{{ mosquitto__ferm__dependent_rules }}'
        - '{{ nginx__ferm__dependent_rules }}'
    - role: python
      tags: [ 'role::python', 'skip::python', 'role::nginx', 'role::mosquitto' ]
      python__dependent_packages3:
        - '{{ nginx__python__dependent_packages3 }}'
        - '{{ mosquitto__python__dependent_packages3 }}'
      python__dependent_packages2:
        - '{{ nginx__python__dependent_packages2 }}'
        - '{{ mosquitto__python__dependent_packages2 }}'
    - role: nginx
      tags: [ 'role::nginx', 'skip::nginx' ]
      nginx__dependent_servers:
        - '{{ mosquitto__nginx__dependent_servers }}'
      nginx__dependent_upstreams:
        - '{{ mosquitto__nginx__dependent_upstreams }}'
    - role: mosquitto
      tags: [ 'role::mosquitto', 'skip::mosquitto' ]

  Ansible tags

You can use Ansible --tags or --skip-tags parameters to limit what tasks are performed during Ansible run. This can be used after host is first configured to speed up playbook execution, when you are sure that most of the configuration has not been changed.

Available role tags:

role::mosquitto
    Main role tag, should be used in the playbook to execute all of the role tasks as well as role dependencies.

role::mosquitto:acl
    Tasks related to ACL configuration.

role::mosquitto:passwd
    Tasks related to user/password management.

role::mosquitto:avahi
    Tasks related to Avahi service support.

role::mosquitto:config
    Tasks related to global configuration.

role::mosquitto:listeners
    Tasks related to mosquitto listener configuration.

role::mosquitto:bridges
    Tasks which configure MQTT bridge connections.

DEFAULT VARIABLE DETAILS

Some of debops.mosquitto default variables have more extensive configuration than simple strings or lists, here you can find documentation and examples for them.

  mosquitto__options

The mosquitto__default_options and mosquitto__options variables are YAML dictionaries which contain global Mosquitto configuration stored in the /etc/mosquitto/conf.d/00_default.conf configuration file. They are combined together, therefore it is possible to override the default options using the Ansible inventory. The Mosquitto configuration reference can be found in the mosquitto.conf(5) <https://manpages.debian.org/mosquitto.conf(5)> manual page.

Each key of the YAML dictionary is a Mosquitto option name. Dictionary keys cannot be specified by variable substitution. If the value is a string, it is set as-is for a given option. If the string value is empty (''), the option will be removed from the configuration file.

If the value is a YAML list, each list element will be present in its own line with the option name prepended to it.

Boolean values are not processed by the template, you should use the string representation accepted by Mosquitto ('true', 'false) in the variable, or use an if/else condition that interprets boolean values and passes correct strings as needed.

  Examples

Set a few custom options for Mosquitto:

# A few variables set in the Ansible inventory as an example
mqtt_connection_messages: False
mqtt_log_dest: 'syslog'
mqtt_log_type: [ 'all', 'debug', 'warning', 'notice' ]
mqtt_log_timestamp: 'true'
mosquitto__options:
  connection_messages: '{{ "true" if mqtt_connection_messages|bool else "false" }}'
  log_dest:            '{{ mqtt_log_dest }}'
  log_type:            '{{ mqtt_log_type if mqtt_log_dest == "syslog" else "" }}'
  log_timestamp:       '{{ mqtt_log_timestamp }}'

The above configuration should result in:

log_timestamp true
connection_messages false
log_dest syslog
log_type all
log_type debug
log_type warning
log_type notice

  mosquitto__listeners

The mosquitto__default_listeners and mosquitto__listeners variables can be used to configure how Mosquitto listens for connections. The variables are YAML dictionaries, which are combined together, therefore the default configuration can be easily changed through Ansible inventory if needed. There's no custom merging, if you want to modify a specific listener, you need to include all of its options.

Each entry in the YAML dictionary is a listener configuration. The dictionary key can be anything, but it's best to specify the TCP port the listener will be configured on, for consistency. The value of the dictionary is another YAML dictionary, with keys being the mosquitto configuration options, and values being the option values; the format is similar to the one used in the mosquitto__options configuration. You can find the possible configuration options and their meaning in the LISTENERS section of the mosquitto.conf(5) <https://manpages.debian.org/mosquitto.conf(5)> manual page.

The role knows about additional listener parameters, which are used to manage the configuration:

comment
    Optional. A custom comment added to the listener, either a string or a YAML text block.

state
    Optional. If not specified or present, the listener configuration will be generated on the host. If absent, the listener configuration will be removed.

    The listener configuration can contain additional parameters that are not used by Mosquitto, but are used to configure Avahi services (see Avahi CNAME (alias) support for more details):

avahi_type
    Required for Avahi support. The string that specifies the service type, for example _mqtt._tcp.

avahi_port
    Required for Avahi support. The port number the service is listening for connections, which will be advertised by Avahi.

avahi_state
    Optional. If not specified or present, the Avahi configuration for a given listener will be generated. If absent, the Avahi configuration for a given listener will be removed.

    You can check the mosquitto__default_listeners variable in the defaults/main.yml file for examples of the Mosquitto listener configuration.

  mosquitto__bridges

The mosquitto__bridges, mosquitto__group_bridges and mosquitto__host_bridges variables can be used to configure bridge connections between MQTT brokers (Mosquitto or other brokers). The variables are YAML dictionaries, which are combined together, therefore the default configuration can be easily changed through Ansible inventory if needed. There's no custom merging, if you want to modify a specific bridge, you need to include all of its options.

Each entry in the YAML dictionary is a bridge configuration. The dictionary key should be a short name of the bridge; it can also be specified as the connection parameter. The value of the dictionary is another YAML dictionary, with keys being the mosquitto configuration options, and values being the option values; the format is similar to the one used in the mosquitto__options configuration. You can find the possible configuration options and their meaning in the CONFIGURING BRIDGES section of the mosquitto.conf(5) <https://manpages.debian.org/mosquitto.conf(5)> manual page.

The role knows about additional bridge parameters, which are used to manage the configuration:

comment
    Optional. A custom comment added to the bridge, either a string or a YAML text block.

state
    Optional. If not specified or present, the bridge configuration will be generated on the host. If absent, the bridge configuration will be removed.

  Examples

The example bridge connection to the test Mosquitto broker <http://test.mosquitto.org> from the manual page:

mosquitto__bridges:
  'test-mosquitto-org':
    address: 'test.mosquitto.org'
    cleansession: 'true'
    topic: 'clients/total in 0 test/mosquitto/org/ $SYS/broker/'

An example two-directional bridge connection to central Mosquitto broker on local domain over TLS:

mosquitto__bridges:
  'mqtt-local':
    address: 'mqtt.{{ ansible_domain }}'
    topic:
      - 'raw both 2 remote/topic/ local/topic/'
      - 'condensed both 2 remote/topic/ local/topic/'
    bridge_cafile: '{{ mosquitto__client_cafile }}'
    bridge_certfile: '{{ mosquitto__client_certfile }}'
    bridge_keyfile: '{{ mosquitto__client_keyfile }}'
    bridge_tls_version: '{{ mosquitto__tls_version }}'

  mosquitto__auth_anonymous

This variable can be used to define Access Control List for anonymous Mosquitto users. It can be either a string (with one entry), a YAML text block (with multiple entries) or a YAML list with string entries.

Each entry should be in the form:

topic [read|write|readwrite] <topic>

The specified entries will be included at the top of the /etc/mosquitto/acl file, therefore will apply to all users that don't provide an username/password, ie. anonymous users.

  Examples

Allow read-only access to the broker status topics for anonymous users:

mosquitto__auth_anonymous:
  - 'topic read $SYS/#'

  mosquitto__auth_users

The mosquitto__auth_users, mosquitto__auth_group_users and mosquitto__auth_host_users can be used to configure user accounts in Mosquitto. Each variable is a YAML list with entries specified as strings which should be th user account names, or as YAML dictionaries that can be used to control the user account configuration.

Batch password generation is supported on Mosquitto 1.4+, therefore user/password entries will only work on older OS releases with upstream Mosquitto, or Debian Stretch. On unsupported systems user accounts won't be registered in the /etc/mosquitto/passwd file, but they can be added or removed manually using the mosquitto_passwd command. The htpasswd Ansible module is not used here because it produces incompatible hash strings.

The following parameters can be included in the YAML dictionary entries:

name
    Required: User account name.

password
    Optional. Password for a given user account. If not specified, a random password will be generated and stored in the secret/ directory in a subdirectory specified in the mosquitto__password_secret_path variable. See debops.secret Ansible role documentation for more details.

acl Optional. Either a string, or a YAML list of entries to configure for a given user account. The format is the same as the anonymous ACL entries.

state
    Optional. If not specified or present, the user account and its ACL entries will be configured on the host. If absent, the user account entry and its ACL entries will be removed from the host.

  Examples

Create a roger user account with custom ACL entry:

mosquitto__auth_users:
  - name: 'roger'
    acl:  'topic foo/bar'

  mosquitto__auth_patterns

This variable can be used to define Access Control List based on topic patterns. It can be either a string (with one entry), a YAML text block (with multiple entries) or a YAML list with string entries.

Each entry should be in the form:

pattern [read|write|readwrite] <topic>

The topics can contain substitutions that are replaced by the broker:

•   %c will match the client-id of a given client

•   %u will match the username of the client

    The specified entries will be included at the bottom of the /etc/mosquitto/acl file. The pattern ACLs apply to all users, even if they have their own specific ACL entries.

  Examples

Allow per-user write access to a given topic:

mosquitto__auth_patterns:
  - 'pattern write sensor/%u/data'

Allow access to bridge connection data depending on the client id:

mosquitto__auth_patterns:
  - 'pattern write $SYS/broker/connection/%c/state'

AUTHOR
Maciej Delmanowski

COPYRIGHT
2014-2022, Maciej Delmanowski, Nick Janetakis, Robin Schneider and others