Access Control (Authentication, Authorisation) in RabbitMQ

This document describes authentication and authorisation machinery that implements access control. Authentication backends should not be confused with authentication mechanisms in AMQP 0-9-1.

A separate guide covers multiple topics around passwords. It is only applicable to the internal authentication backend.

Terminology and Definitions

Authentication and authorisation are often confused or used interchangeably. That's wrong and in RabbitMQ, the two are separated. For the sake of simplicity, we'll define authentication as "identifying who the user is" and authorisation as "determining what the user is and isn't allowed to do."

Default Virtual Host and User

When the server first starts running, and detects that its database is uninitialised or has been deleted, it initialises a fresh database with the following resources:

  • a virtual host named /
  • a user named guest with a default password of guest, granted full access to the / virtual host.

It is advisable to delete the guest user or change the password to a password that's not publicly known.

"guest" user can only connect via localhost

By default, the guest user is prohibited from connecting to the broker remotely; it can only connect over a loopback interface (i.e. localhost). This applies both to AMQP 0-9-1 and to any other protocols enabled via plugins. Any other users you create will not (by default) be restricted in this way.

The recommended way to address this in production systems is to create a new user or set of users with the permissions to access the necessary virtual hosts. This can be done using CLI tools, HTTP API or definitions import.

This is configured via the loopback_users item in the configuration file.

If you wish to allow the guest user to connect from a remote host, you should set the loopback_users configuration to none. A complete RabbitMQ config file which does this would look like:

loopback_users = none
Or, in the classic config file format (rabbitmq.config):
[{rabbit, [{loopback_users, []}]}].

How Permissions Work

When a RabbitMQ client establishes a connection to a server, it specifies a virtual host within which it intends to operate. A first level of access control is enforced at this point, with the server checking whether the user has any permissions to access the virtual hosts, and rejecting the connection attempt otherwise.

Resources, i.e. exchanges and queues, are named entities inside a particular virtual host; the same name denotes a different resource in each virtual host. A second level of access control is enforced when certain operations are performed on resources.

RabbitMQ distinguishes between configure, write and read operations on a resource. The configure operations create or destroy resources, or alter their behaviour. The write operations inject messages into a resource. And the read operations retrieve messages from a resource.

In order to perform an operation on a resource the user must have been granted the appropriate permissions for it. The following table shows what permissions on what type of resource are required for all the AMQP commands which perform permission checks.

AMQP 0-9-1 Operation configure write read
exchange.declare(with AE)exchangeexchange (AE)exchange
queue.declare(with DLX)queueexchange (DLX)queue
exchange.bindexchange (destination)exchange (source)
exchange.unbindexchange (destination)exchange (source)

Permissions are expressed as a triple of regular expressions - one each for configure, write and read - on per-vhost basis. The user is granted the respective permission for operations on all resources with names matching the regular expressions. (Note: For convenience RabbitMQ maps AMQP's default exchange's blank name to 'amq.default' when performing permission checks.)

The regular expression '^$', i.e. matching nothing but the empty string, covers all resources and effectively stops the user from performing any operation. Standard AMQP resource names are prefixed with amq. and server generated names are prefixed with amq.gen. For example, '^(amq\.gen.*|amq\.default)$' gives a user access to server-generated names and the default exchange. The empty string, '' is a synonym for '^$' and restricts permissions in the exact same way.

RabbitMQ may cache the results of access control checks on a per-connection or per-channel basis. Hence changes to user permissions may only take effect when the user reconnects.

For details of how to set up access control, please see the Access Control section of the rabbitmqctl man page.

User Tags and Management UI Access

In addition to the permissions covered above, users can have tags associated with them. Currently only management UI access is controlled by user tags.

The tags are managed using rabbitmqctl. Newly created users do not have any tags set on them by default.

Please refer to the management plugin guide to learn more about what tags are supported and how they limit management UI access.

Topic Authorisation

As of version 3.7.0, RabbitMQ supports topic authorisation for topic exchanges. The routing key of a message published to a topic exchange is taken into account when publishing authorisation is enforced (e.g. in RabbitMQ default authorisation backend, the routing key is matched against a regular expression to decide whether the message can be routed downstream or not). Topic authorisation targets protocols like STOMP and MQTT, which are structured around topics and use topic exchanges under the hood.

Topic authorisation is an additional layer on top of existing checks for publishers. Publishing a message to a topic-typed exchange will go through both the basic.publish and the routing key checks. The latter is never called if the former refuses access.

Topic authorisation can also be enforced for topic consumers. Note that it works different for different protocols. The concept of topic authorisation only really makes sense for the topic-oriented protocols such as MQTT and STOMP. In AMQP 0-9-1, for example, consumers consume from queues and thus the standard resource permissions apply. In addition for AMQP 0-9-1, binding routing keys between an AMQP 0-9-1 topic exchange and a queue/exchange are checked against the topic permissions configured, if any. For more information about how RabbitMQ handles authorization for topics, please see the STOMP and MQTT documentation guides.

When default authorisation backend is used, publishing to a topic exchange or consuming from a topic is always authorised if no topic permissions are defined (which is the case on a fresh RabbitMQ installation). With this authorisation backend, topic authorisation as optional: you don't need to whitelist any exchanges. To use topic authorisation therefore you need to opt in and define topic permissions for one or more exchanges. For details please see the Access Control section of the rabbitmqctl man page.

Internal (default) authorisation backend supports variable expansion in permission patterns. Three variables are supported: username, vhost, and client_id. Note that client_id only applies to MQTT. For example, if tonyg is the connected user, the permission ^{username}-.* is expanded to ^tonyg-.*

If a different authorisation backend (e.g. LDAP, HTTP, AMQP) is used, please refer to the documentation of those backends.

If a custom authorisation backend is used, topic authorisation is enforced by implementing the check_topic_access callback of the rabbit_authz_backend behavior.

Alternative Authentication and Authorisation Backends

Authentication and authorisation are pluggable. Plugins can provide implementations of

  • authentication ("authn") backends
  • authorisation ("authz") backends
It is possible for a plugin to provide both. For example the internal, LDAP and HTTP backends do so.

Some plugins, for example, the Source IP range one, only provide an authorisation backend. Authentication is supposed to be handled by the internal database, LDAP, etc.

Combining Backends

It is possible to use multiple backends for authn or authz using the auth_backends configuration key. When several authentication backends are used then the first positive result returned by a backend in the chain is considered to be final. This should not be confused with mixed backends (for example, using LDAP for authentication and internal backend for authorisation).

The following example configures RabbitMQ to use the internal backend only (and is the default):

# rabbitmq.conf
# 1 here is a backend name. It can be anything.
# Since we only really care about backend
# ordering, we use numbers throughout this guide.
# "internal" is an alias for rabbit_auth_backend_internal
auth_backends.1 = internal
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [rabbit_auth_backend_internal]}

The example above uses an alias, internal for rabbit_auth_backend_internal. The following aliases are available:

When using third party plugins, providing a full module name is necessary.

The following example configures RabbitMQ to use the LDAP backend for both authentication and authorisation. Internal database will not be consulted:

auth_backends.1 = ldap
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [rabbit_auth_backend_ldap]}

This will check LDAP first, and then fall back to the internal database if the user cannot be authenticated through LDAP:

auth_backends.1 = ldap
auth_backends.2 = internal
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_internal]}

Same as above but will fall back to the HTTP backend instead:

# rabbitmq.conf
auth_backends.1 = ldap
# uses module name instead of a short alias, "http"
auth_backends.2 = rabbit_auth_backend_http

# See HTTP backend docs for details
auth_http.user_path = http://my-authenticator-app/auth/user
auth_http.vhost_path = http://my-authenticator-app/auth/vhost
auth_http.resource_path = http://my-authenticator-app/auth/resource
auth_http.topic_path = http://my-authenticator-app/auth/topic
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_http]}
 %% See HTTP backend docs for details
   [{user_path,     "http://my-authenticator-app/auth/user"},
    {vhost_path,    "http://my-authenticator-app/auth/vhost"},
    {resource_path, "http://my-authenticator-app/auth/resource"},
    {topic_path,    "http://my-authenticator-app/auth/topic"}]}].

The following example configures RabbitMQ to use the internal database for authentication and the source IP range backend for authorisation:

# rabbitmq.conf
auth_backends.1.authn = internal
# uses module name because this backend is from a 3rd party
auth_backends.1.authz = rabbit_auth_backend_ip_range
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [{rabbit_auth_backend_internal, rabbit_auth_backend_ip_range}]}

The following example configures RabbitMQ to use the LDAP backend for authentication and the internal backend for authorisation:

# rabbitmq.conf
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal}]

The example below is fairly advanced. It will check LDAP first. If the user is found in LDAP then the password will be checked against LDAP and subsequent authorisation checks will be performed against the internal database (therefore users in LDAP must exist in the internal database as well, but do not need a password there). If the user is not found in LDAP then a second attempt is made using only the internal database:

# rabbitmq.conf
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
auth_backends.2       = internal
Or, in the classic config format:
[{rabbit, [
            {auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal},

Getting Help and Providing Feedback

If you have questions about the contents of this guide or any other topic related to RabbitMQ, don't hesitate to ask them on the RabbitMQ mailing list.

Help Us Improve the Docs <3

If you'd like to contribute an improvement to the site, its source is available on GitHub. Simply fork the repository and submit a pull request. Thank you!