Skip to main content

Authentication, Authorisation, Access Control

Overview

This document describes authentication and authorisation features in RabbitMQ. Together they allow the operator to control access to the system.

Different users can be granted access only to specific virtual hosts. Their permissions in each virtual host also can be limited.

RabbitMQ supports two major authentication mechanisms as well as several authentication and authorisation backends.

This guide covers a variety of authentication, authorisation and user management topics such as

Password-based authentication has a companion guide. A closely related topic of TLS support is also covered in a dedicated guide.

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."

The Basics

Clients use RabbitMQ features to connect to it. Every connection has an associated user which is authenticated. It also targets a virtual host for which the user must have a certain set of permissions.

User credentials, target virtual host and (optionally) client certificate are specified at connection initiation time.

There is a default pair of credentials called the default user. This user can only be used for host-local connections by default. Remote connections that use it will be refused.

Production environments should not use the default user and create new user accounts with generated credentials instead.

Default Virtual Host and User

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

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

If a blank node imports definitions on boot, this default user will not be created.

It is highly recommended to pre-configure a new user with a generated username and password or delete the guest user or at least change its password to reasonably secure generated value that won't be known to the public.

Authentication: Who Do You Say You Are?

After an application connects to RabbitMQ and before it can perform operations, it must authenticate, that is, present and prove its identity. With that identity, RabbitMQ nodes can look up its permissions and authorize access to resources such as virtual hosts, queues, exchanges, and so on.

Two primary ways of authenticating a client are username/password pairs and X.509 certificates. Username/password pairs can be used with a variety of authentication backends that verify the credentials.

Connections that fail to authenticate will be closed with an error message in the server log.

Authentication using Client TLS (x.509) Certificate Data

To authenticate client connections using X.509 certificate a built-in plugin, rabbitmq-auth-mechanism-ssl, must be enabled and clients must be configured to use the EXTERNAL mechanism. With this mechanism, any client-provided password will be ignored.

"guest" user can only connect from localhost

By default, the guest user is prohibited from connecting from remote hosts; it can only connect over a loopback interface (i.e. localhost). This applies to connections regardless of the protocol. Any other users 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.

It is possible to allow the guest user to connect from a remote host by setting the loopback_users configuration to none.

A minimalistic RabbitMQ config file which allows remote connections for guest looks like so:

# DANGER ZONE!
#
# allowing remote connections for default user is highly discouraged
# as it dramatically decreases the security of the system. Delete the user
# instead and create a new one with generated secure credentials.
loopback_users = none

Managing Users and Permissions

Users and permissions can be managed using CLI tools and definition import (covered below).

Before We Start: Shell Escaping and Generated Passwords

It is a common security practice to generate complex passwords, often involving non-alphanumeric characters. This practice is perfectly applicable to RabbitMQ users.

Shells (bash, zsh, and so on) interpret certain characters (!, ?, &, ^, ", ', *, ~, and others) as control characters.

When a password is specified on the command line for rabbitmqctl add_user, rabbitmqctl change_password, and other commands that accept a password, such control characters must be escaped appropriately for the shell used. With inappropriate escaping the command will fail or RabbitMQ CLI tools will receive a different value from the shell.

When generating passwords that will be passed on the command line, long (say, 40 to 100 characters) alphanumeric value with a very limited set of symbols (e.g. :, =) is the safest option.

When users are created via HTTP API without using a shell (e.g. curl), the control character limitation does not apply. However, different escaping rules may be necessary depending on the programming language used.

Adding a User

To add a user, use rabbitmqctl add_user. It has multiple ways of specifying a password:

# will prompt for password, only use this option interactively
rabbitmqctl add_user "username"
# Password is provided via standard input.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
echo '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b' | rabbitmqctl add_user 'username'
# Password is provided as a command line argument.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
rabbitmqctl add_user 'username' '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b'

On Windows, rabbitmqctl becomes rabbitmqctl.bat and shell escaping would be different:

# password is provided as a command line argument
rabbitmqctl.bat add_user 'username' '9a55f70a841f18b97c3a7db939b7adc9e34a0f1d'

Listing Users

To list users in a cluster, use rabbitmqctl list_users:

rabbitmqctl list_users
rabbitmqctl.bat list_users

The output can be changed to be JSON:

rabbitmqctl list_users --formatter=json
rabbitmqctl.bat list_users --formatter=json

Deleting a User

To delete a user, use rabbitmqctl delete_user:

rabbitmqctl delete_user 'username'
rabbitmqctl.bat delete_user 'username'

Granting Permissions to a User

To grant permissions to a user in a virtual host, use rabbitmqctl set_permissions:

# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl set_permissions -p "custom-vhost" "username" ".*" ".*" ".*"
# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl.bat set_permissions -p 'custom-vhost' 'username' '.*' '.*' '.*'

Clearing Permissions of a User in a Virtual Host

To revoke permissions from a user in a virtual host, use rabbitmqctl clear_permissions:

# Revokes permissions in a virtual host
rabbitmqctl clear_permissions -p "custom-vhost" "username"
# Revokes permissions in a virtual host
rabbitmqctl.bat clear_permissions -p 'custom-vhost' 'username'

Operations on Multiple Virtual Hosts

Every rabbitmqctl permission management operation is scoped to a single virtual host. Bulk operations have to be scripted, with the list of virtual hosts coming from rabbitmqctl list_vhosts --silent:

# Assumes a Linux shell.
# Grants a user permissions to all virtual hosts.
for v in $(rabbitmqctl list_vhosts --silent); do rabbitmqctl set_permissions -p $v "a-user" ".*" ".*" ".*"; done

Seeding (Pre-creating) Users and Permissions

Production environments typically need to pre-configure (seed) a number of virtual hosts, users and user permissions.

This can be done in a few ways:

CLI Tools

See the section on User Management.

Definition Import at Node Boot Time

This process involves the following steps:

  • Set up a temporary node and create the necessary virtual host, users, permissions, and so on using CLI tools
  • Export definitions to a definition file
  • Remove parts of the file that are not relevant
  • Configure the node to import the file on node boot or after

See importing definitions on node boot in the definitions guide to learn more.

Definition Import After Node Boot

See importing definitions after node boot in the definitions guide.

Override Default User Credentials

Two configuration options can be used to override default user credentials. This user will only be created on first node boot so they must exist in the configuration file before first boot.

The settings are:

# default is "guest", and its access is limited to localhost only.
# See ./access-control#default-state
default_user = a-user
# default is "guest"
default_pass = 768a852ed69ce916fa7faa278c962de3e4275e5f

As with all values in rabbitmq.conf, the # character starts a comment so this character must be avoided in generated credentials.

Default user credentials can also be encrypted. That requires the use of the advanced configuration file, advanced.config. This topic is covered in more detail in Configuration Value Encryption.

Authorisation: How Permissions Work

When a RabbitMQ client establishes a connection to a server and authenticates, 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 Operationconfigurewriteread
exchange.declare(passive=false)exchange
exchange.declare(passive=true)
exchange.declare(with AE)exchangeexchange (AE)exchange
exchange.deleteexchange
queue.declare(passive=false)queue
queue.declare(passive=true)
queue.declare(with DLX)queueexchange (DLX)queue
queue.deletequeue
exchange.bindexchange (destination)exchange (source)
exchange.unbindexchange (destination)exchange (source)
queue.bindqueueexchange
queue.unbindqueueexchange
basic.publishexchange
basic.getqueue
basic.consumequeue
queue.purgequeue

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.

For convenience RabbitMQ maps AMQP 0-9-1'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. Built-in AMQP 0-9-1 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 User management section as well as 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

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 differently 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 authorisation 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 is optional: you don't need to approve 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 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, OAuth 2) 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.

Revoking User Access and Credential Rotation

Revoking User Access

To revoke user access, the recommended procedure is deleting the user. All open connections that belong to a deleted user will be closed.

It is also possible to clear user permissions but that will not affect any currently open connections. Connections use an authorization operation cache, so client operations will be refused eventually. The period of time depends on the authorization backend used.

Credential Rotation

A credential rotation for credentials of users stored in the internal data store usually involves the following steps:

  • changing user password using CLI tools or update it using the PUT /api/users/{user} HTTP API endpoint
  • Using rabbitmqctl close_all_user_connections or rabbitmqctl close_all_connections to close all existing connections
  • Making sure that applications can discover the new credentials and reconnect, for example, by restarting them

In case of a suspected credential leak, a more thorough credential rotation procedure can be employed:

  • Assessing the permissions of the user and obtaining a complete cluster definitions file
  • Delete the user, thus revoking its access and closing all existing connections that used those credentials
  • Re-adding the user with a new generated password
  • Re-granting the user access to all the virtual hosts it had access to earlier
  • Making sure that applications can discover the new credentials and reconnect, for example, by restarting them

With external authN backends such as LDAP, user accounts are managed externally to RabbitMQ, therefore the credential rotation routine will also be external to RabbitMQ.

Authentication and Authorisation Backends

Authentication and authorisation are pluggable. Plugins can provide implementations of:

  • authentication ("authn") backends: they determine client identity and decide whether the client should be allowed to connect
  • authorisation ("authz") backends: they determine whether an identified (authenticated) client is authorized to perform a certain operation

It is possible and common for a plugin to provide both backends. RabbitMQ ships with the following built-in plugins which provide both authentication and authorisation backends:

The following built-in plugins provide authorisation backend implementations:

Some plugins such as Source IP range one also only provide an authorisation backend.

Authentication is supposed to be handled by the internal database, LDAP, etc.

A special cache backend can be used in combination with other backends to significantly reduce the load they generate on external services, such as LDAP or HTTP servers.

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

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

Some plugins do not currently have a shortcut. In this case, a full module (not the name of the plugin!) must be used:

# note that the module name begins with a "rabbit_", not "rabbitmq_", like in the name
# of the plugin
auth_backends.1 = rabbit_auth_backend_oauth2

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

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

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

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

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

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

Authentication Mechanisms

RabbitMQ supports multiple SASL authentication mechanisms. There are three such mechanisms built into the server: PLAIN, AMQPLAIN, and RABBIT-CR-DEMO, and one — EXTERNAL — available as a plugin.

More authentication mechanisms can be provided by plugins. See the plugin development guide for more information on general plugin development.

Built-in Authentication Mechanisms

The built-in mechanisms are:

MechanismDescription
PLAIN

SASL PLAIN authentication. This is enabled by default in the RabbitMQ server and clients, and is the default for most other clients.

AMQPLAIN

Non-standard version of PLAIN retained for backwards compatibility. This is enabled by default in the RabbitMQ server.

EXTERNAL

Authentication happens using an out-of-band mechanism such as x509 certificate peer verification, client IP address range, or similar. Such mechanisms are usually provided by RabbitMQ plugins.

RABBIT-CR-DEMONon-standard mechanism which demonstrates challenge-response authentication. This mechanism has security equivalent to PLAIN, and is not enabled by default in the RabbitMQ server.

Mechanism Configuration in the Server

The configuration variable auth_mechanisms in the rabbit application determines which of the installed mechanisms are offered to connecting clients. This variable should be a list of atoms corresponding to mechanism names, for example ['PLAIN', 'AMQPLAIN'] by default. The server-side list is not considered to be in any particular order. See the configuration file documentation.

Mechanism Configuration in the Client

Applications must opt-in to use a different authentication mechanism such as EXTERNAL.

Mechanism Configuration in Java

The Java client does not use the javax.security.sasl package by default since this can be unpredictable on non-Oracle JDKs and is missing entirely on Android. There is a RabbitMQ-specific SASL implementation, configured by the SaslConfig interface. A class DefaultSaslConfig is provided to make SASL configuration more convenient in the common case. A class JDKSaslConfig is provided to act as a bridge to javax.security.sasl.

ConnectionFactory.getSaslConfig() and ConnectionFactory.setSaslConfig(SaslConfig) are the primary methods for interacting with authentication mechanisms.

Mechanism Configuration in .NET

The .NET client provides its own SASL mechanism implementations based on the AuthMechanism and AuthMechanismFactory interfaces. The ConnectionFactory.AuthMechanisms property is a list of authentication mechanism factories in preference order.

Mechanism Configuration in Erlang

The Erlang client provides its own SASL mechanism implementations in the amqp_auth_mechanisms module. The #amqp_params record can be provided with a list of authentication functions in preference order for network connections.

Troubleshooting Authentication

Server logs will contain entries about failed authentication attempts:

2019-03-25 12:28:19.047 [info] <0.1613.0> accepting AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)
2019-03-25 12:28:19.056 [error] <0.1613.0> Error on AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672, state: starting):
PLAIN login refused: user 'user2' - invalid credentials
2019-03-25 12:28:22.057 [info] <0.1613.0> closing AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)

Authentication failures on connections that authenticate using X.509 certificates will be logged differently. See TLS Troubleshooting guide for details.

rabbitmqctl authenticate_user can be used to test authentication for a username and password pair:

rabbitmqctl authenticate_user "a-username" "a/password"

If authentication succeeds, it will exit with the code of zero. In case of a failure, a non-zero exit code will be used and a failure error message will be printed.

rabbitmqctl authenticate_user will use a CLI-to-node communication connection to attempt to authenticate the username/password pair against an internal API endpoint. The connection is assumed to be trusted. If that's not the case, its traffic can be encrypted using TLS.

Per AMQP 0-9-1 spec, authentication failures should result in the server closing TCP connection immediately. However, with RabbitMQ clients can opt in to receive a more specific notification using the authentication failure notification extension to AMQP 0-9-1. Modern client libraries support that extension transparently to the user: no configuration would be necessary and authentication failures will result in a visible returned error, exception or other way of communicating a problem used in a particular programming language or environment.

Troubleshooting Authorisation

rabbitmqctl list_permissions can be used to inspect a user's permission in a given virtual host:

rabbitmqctl list_permissions --vhost /
# => Listing permissions for vhost "/" ...
# => user configure write read
# => user2 .* .* .*
# => guest .* .* .*
# => temp-user .* .* .*

rabbitmqctl list_permissions --vhost gw1
# => Listing permissions for vhost "gw1" ...
# => user configure write read
# => guest .* .* .*
# => user2 ^user2 ^user2 ^user2

Server logs will contain entries about operation authorisation failures. For example, if a user does not have any permissions configured for a virtual host:

2019-03-25 12:26:16.301 [info] <0.1594.0> accepting AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672)
2019-03-25 12:26:16.309 [error] <0.1594.0> Error on AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, user: 'user2', state: opening):
access to vhost '/' refused for user 'user2'
2019-03-25 12:26:16.310 [info] <0.1594.0> closing AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, vhost: 'none', user: 'user2')

authorisation failures (permission violations) are also logged:

2019-03-25 12:30:05.209 [error] <0.1627.0> Channel error on connection <0.1618.0> (127.0.0.1:63881 -> 127.0.0.1:5672, vhost: 'gw1', user: 'user2'), channel 1:
operation queue.declare caused a channel exception access_refused: access to queue 'user3.q1' in vhost 'gw1' refused for user 'user2'