[TOC] ## Introduction The Dynamic Security plugin is a Mosquitto plugin which provides role based authentication and access control features that can updated whilst the broker is running, using a special topic based API. It is supported since Mosquitto 2.0, and should be available in all installations, but will not be activated by default. ## Concepts This section describes the concepts of how the plugin operates. If you want to find out how to use the plugin features, look in the [Usage] section below. The plugin allows you to create three main objects, `clients`, `groups`, and `roles`. This document will use the term `clients` to mean the clients defined in the plugin, and `devices` or `users` to mean the MQTT clients that connect to the broker. --- ### Clients When you want a device or user to be able to connect and authenticate to the broker, you create a client. Each client has the following attributes: #### Username The client username maps to the username provided in the CONNECT packet when a device connects. The username is unique across the plugin, so attempting to create a client with a duplicate username will result in an error. The username acts as the primary key if you want to change anything about the client. #### Password The client password maps to the password provided in the CONNECT packet when a device connects. The password may be unset when a client is created, this will mean that devices will be unable to connect as the corresponding client. The password can be updated at any point, but only by a client with the correct access. Devices typically cannot update their own passwords. #### Client ID The client id maps to the client id provided in the CONNECT packet when a device connects. This is an optional attribute. If the client id is empty or not provided, then any device can connect with the username for this client regardless of its client id. This means that multiple devices could connect with the same credentials at the same time, but sharing credentials between devices is not recommended. If the client id is set, then a device can only connect as this client if the triple of username, password, and client id all match those in the client. #### Groups A client can be a member of any number of groups. #### Roles A client can be assigned to any number of roles. A role gives the client access to different topics. #### Text name This is an optional text field to give a human friendly name to this client. #### Text description This is an optional text field to give a human friendly description to this client. #### Disabled A client can be set to be enabled/disabled at any point. Disabling a client means that any devices currently connected using the credentials for that client will be disconnected and unable to reconnect. --- ### Groups Multiple clients can be placed in a group. Groups can have roles assigned to them, so using groups is appropriate where you have a number of clients that need to have the same access. Groups have the following attributes: #### Group name The group name is the primary name for the group. It is used when modifying the group in any way, such as adding a client or a role. #### Roles A group can be assigned to any number of roles. A role gives the group access to different topics. #### Text name This is an optional text field to give a human friendly name to this group. #### Text description This is an optional text field to give a human friendly description to this group. --- ### Roles Roles contain multiple access control lists (ACLs), and can be assigned to clients and/or groups. Roles have the following attributes: #### Role name The role name is the primary name for the role. It is used when modifying the role in any way, such as adding an ACL. #### Access Control Lists ACLs are the feature which allows access to topics to be controlled. Checks are made on different events as they happen: `publishClientSend`, `publishClientReceive`, `subscribe`, and `unsubscribe`. The `publishClientSend` event occurs when a device sends a PUBLISH message to the broker, i.e. "is the device allowed to publish to this topic". The `publishClientReceive` event occurs when a device is due to receive a PUBLISH message from the broker, i.e. it has a valid subscription and a matching message has been published to the broker. The `subscribe` event occurs in response to a device sending a SUBSCRIBE message, and the `unsubscribe` event occurs in response to a device sending an UNSUBSCRIBE packet. The default behaviour of the different events can be set to allow or deny access. The default behaviour applies if no matching ACL is found. The default behaviour for the different events when the plugin has first been configured is: * `publishClientSend`: deny * `publishClientReceive`: allow * `subscribe`: deny * `unsubscribe`: allow There is some overlap between `publishClientReceive` and `subscribe`. In most cases, using a `subscribe` ACL is sufficient to provide the control you need, however by combining the two types it is possible to e.g. allow subscriptions to a wildcard topic like `topic/#`, but deny access for device to receive messages on a specific topic within that hierarchy like 'topic/secret'. The different events have ACL types associated with them, and it is these ACLs that you will add to your roles. Each ACL has a `topic`, a `priority`, and can be set to `allow` or `deny`. The `publishClientSend` and `publishClientReceive` ACL types map directly to the events of the same name. The topic can contain wildcards, so allowing send access to `topic/#` will allow devices to publish to all topics in the `topic/#` hierarchy, including `topic`. The `subscribe` and `unsubscribe` events have two ACL types each: `subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and `unsubscribePattern`. The `*Literal` ACL types make a literal comparison between the topic filter provided for the ACL and the topic filter provided in the SUBSCRIBE or UNSUBSCRIBE message. This means that setting a `subscribeLiteral` ACL with topic filter `#` to deny would prevent matching devices from subscribing the the `#` topic filter only, but still allow them to subscribe to `topic/#`, for example. The `*Pattern` ACL types allow or deny access based on a wildcard comparison of the ACL topic filter and the topic provided in the SUBSCRIBE or UNSUBSCRIBE message. This means that setting a `subscribePattern` ACL with topic filter `#` to deny would prevent matching devices from subscribing to any topic at all. #### Text name This is an optional text field to give a human friendly name to this role. #### Text description This is an optional text field to give a human friendly description to this role. --- ### Priorities If you are working with more than one role per client or group, or more than one group per client, then it is crucial to understand how roles and ACLs are applied. The order in which checks are made is determined in part by the `priority` of groups, roles and ACLs. Each client group has a priority, each client role and group role has a priority, and each ACL within a role has a priority. If not set explicitly, priorities will default to -1. For each of the group, role, and ACL objects, checks are made in priority order from the highest numerical value to the lowest numerical value. If two objects of the same type have the same priority, then they will be checked in lexographical order according to the username/groupname/rolename, but it is advised to use unique priorities per object type. When an event occurs that needs an ACL check, the ACLs for that ACL type are checked in order until there is a matching ACL for the topic in question. Within each role that is checked, the ACLs are checked in priority order. The roles assigned to a client are checked first, in priority order. Each client group is checked in priority order, with all of the roles in a group being checked in priority order before the next group is checked. As an example, let us assume we have the following client, groups, and roles: Client: `sensor` Groups: `temperature` (priority 2), `humidity` (priority 1) Roles: `hallway` Group: `temperature` Roles: `input` (priority 5), `output` (priority 1) Group: `humidity` Roles: `humidity` Role: `hallway` ACLs: `Z` (priority 3), `A` (priority 1) Role: `input` ACLs: `Z` (priority 3), `A` (priority 3) Role: `output` ACLs: `Z` (priority 3), `A` (priority 1) Role: `humidity` ACLs: `Z` (priority 3), `A` (priority 1) We are also assuming we are only looking at single ACL type. If our client `sensor` triggers an ACL check, the ACLs will be checked in this order, and the first matching ACL will be used to allow/reject the event: 1. sensor/hallway Z 2. sensor/hallway A 3. temperature/input A (alphabetical sort) 4. temperature/input Z (alphabetical sort) 5. temperature/output Z 6. temperature/output A 7. humidity/humidity Z 8. humidity/humidity A This is provided as an example that covers all combinations of roles, it is recommended to use as simple a setup as possible for your situation. ### Anonymous access All of the documentation so far assumes that you do not allow anonymous unauthenticated access - meaning devices or users that connect without a username. You may wish to allow anonymous access, but still make use of the dynamic security plugin, and this is supported through the automatic anonymous group. If allowed, anything connecting without a username will be assigned to a group that you define. By assigning roles to that group, you can control what anonymous devices can access. ## Initial configuration To use the Dynamic Security plugin, it must be configured in the broker and an initial plugin configuration must be generated. To configure the broker, add the following to your configuration file. Linux/BSD: ``` plugin path/to/mosquitto_dynamic_security.so plugin_opt_config_file path/to/dynamic-security.json ``` Windows: ``` plugin path\to\mosquitto_dynamic_security.dll plugin_opt_config_file path\to\dynamic-security.json ``` It is recommended to use `per_listener_settings false` with this plugin, so all listeners use the same authentication and access control. The `dynamic-security.json` file is where the plugin configuration will be stored. To generate an initial file, use the `mosquitto_ctrl` utility. ``` mosquitto_ctrl dynsec init path/to/dynamic-security.json admin-user ``` Choose your own `admin-user` username. You will be asked for a password for the client. This user will be assigned the `admin` role, which has the following access: * publishClientSend: `$CONTROL/dynamic-security/#` - this allows the client to control the Dynamic security plugin. * publishClientReceive: `$CONTROL/dynamic-security/#` - this allows the client to receive information from the plugin. This is not necessary in the default configuration, but is included in case the default behaviour for `publishClientReceive` is set to `deny`. * subscribePattern: `$CONTROL/dynamic-security/#` - this allows the client to receive information from the plugin. * publishClientReceive: `$SYS/#` - this allows the client to see the broker metrics. * subscribePattern: `$SYS/#` - this allows the client to see the broker metrics. * publishClientReceive: `#` - this allows the client to examine the messages being published by other clients. * subscribePattern: `#` - this allows the client to examine the messages being published by other clients. * unsubscribePattern: `#` - this allows the client to undo previous subscriptions. This is not necessary in the default configuration, but is included in case the default behaviour for `unsubscribe` is set to `deny`. The admin user does not have access to publish to normal application topics in the `#` hierarchy by default. You are strongly encouraged to keep the admin user purely for administering the plugin, and create other clients for your application. ## Using mosquitto_ctrl with a running broker The initial configuration is the only time that `mosquitto_ctrl` does not connect to a broker to carry out the configuration. All other commands require a connection to a broker, and hence a username, password, and whatever else is required for that particular connection. The connection options must be given before the `dynsec` part of the command line: ``` mosquitto_ctrl dynsec ... ``` For example: ``` mosquitto_ctrl -u admin -h localhost dynsec ... ``` It is possible to provide the admin password on the command line, but this is not recommended. See **FIXME** for the full list of options available for `mosquitto_ctrl`. ## Configuring default access The initial configuration sets the default ACL type behaviours to: * `publishClientSend`: deny * `publishClientReceive`: allow * `subscribe`: deny * `unsubscribe`: allow If you wish to change these, use `mosquitto_ctrl`. ``` mosquitto_ctrl dynsec setDefaultACLAccess publishClientSend deny mosquitto_ctrl dynsec setDefaultACLAccess publishClientReceive deny mosquitto_ctrl dynsec setDefaultACLAccess subscribe deny mosquitto_ctrl dynsec setDefaultACLAccess unsubscribe deny ``` You can examine the current default access with the `getDefaultACLAccess` command: ``` mosquitto_ctrl dynsec getDefaultACLAccess unsubscribe ``` ## Creating and modifying clients To create a new client: ``` mosquitto_ctrl dynsec createClient ``` This creates a client which does not have a client id associated with it. You will be asked for the password for the new client before you are asked for the admin user password. Pay attention to the messages on the command line. ``` mosquitto_ctrl dynsec createClient -i ``` This creates a client which has a client id associated with it. To delete a client (clients connected with these credentials will be disconnected from the broker): ``` mosquitto_ctrl dynsec deleteClient ``` To disable a client (clients connected with these credentials will be disconnected from the broker): ``` mosquitto_ctrl dynsec disableClient ``` To enable a client (clients will be able to use these credentials to log in again): ``` mosquitto_ctrl dynsec enableClient ``` To set a client password: ``` mosquitto_ctrl dynsec setClientPassword mosquitto_ctrl dynsec setClientPassword ``` To add/remove a role to/from a client: ``` mosquitto_ctrl dynsec addClientRole mosquitto_ctrl dynsec removeClientRole ``` To get information on a client: ``` mosquitto_ctrl dynsec getClient ``` To list all clients: ``` mosquitto_ctrl dynsec listClients ``` The `modifyClient` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`. ## Creating and modifying groups To create a new group: ``` mosquitto_ctrl dynsec createGroup ``` To delete a group: ``` mosquitto_ctrl dynsec deleteGroup ``` To add/remove a client to/from a group: ``` mosquitto_ctrl dynsec addGroupClient mosquitto_ctrl dynsec removeGroupClient ``` In this case the `priority` refers to the priority of the group within the client's list of groups. To add/remove a role to/from a group: ``` mosquitto_ctrl dynsec addGroupRole mosquitto_ctrl dynsec removeGroupRole ``` To set/get the group that anonymous devices are assigned to: ``` mosquitto_ctrl dynsec setAnonymousGroup mosquitto_ctrl dynsec getAnonymousGroup ``` To get information on a group: ``` mosquitto_ctrl dynsec getGroup ``` To list all groups: ``` mosquitto_ctrl dynsec listGroups ``` The `modifyGroup` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`. ## Creating and modifying roles To create a new role: ``` mosquitto_ctrl dynsec createRole ``` To delete a role: ``` mosquitto_ctrl dynsec deleteRole ``` To add an ACL to a role: ``` mosquitto_ctrl dynsec addRoleACL allow|deny ``` Where `acltype` is one of `publishClientSend`, `publishClientReceive`, `subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and `unsubscribePattern`. For example: ``` mosquitto_ctrl dynsec addRoleACL clientPublishSend client/topic allow 5 ``` To remove an ACL from a role using the topic filter as the key: ``` mosquitto_ctrl dynsec removeRoleACL ``` For example: ``` mosquitto_ctrl dynsec removeRoleACL clientPublishSend client/topic ``` To get information on a role: ``` mosquitto_ctrl dynsec getRole ``` To list all roles: ``` mosquitto_ctrl dynsec listRoles ``` The `modifyRole` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`.