Skip to content

Scope RabbitMQ virtual hosts and permissions

Severity: highApplies to: RabbitMQ 3.x / 4.x
The fix
Terminal window
# A vhost per application, and a user scoped to it
rabbitmqctl add_vhost orders
rabbitmqctl add_user orders_app "$(openssl rand -base64 24)"
# configure / write / read — each a regex of resource names
rabbitmqctl set_permissions -p orders orders_app '^orders\.' '^orders\.' '^orders\.'

By default the replacement for guest is often a single user with .* .* .* on the default vhost / — full control of everything. That’s guest with a different name.

A leaked or compromised broker credential with full permissions can read every message on every queue, delete queues, reconfigure exchanges to redirect messages, and set up its own bindings to siphon traffic. Message brokers sit between your services, so control of the broker is control of the data flowing between them.

Scoping cuts that down. A credential limited to one virtual host and one set of resource names can touch only its own application’s messaging — a leak is bounded to that one app, not the whole estate.

A virtual host is a fully separate namespace: its own exchanges, queues, bindings and permissions. A user granted access to vhost orders cannot see anything in vhost billing — not the queues, not the messages, not that it exists.

That makes vhosts the right separation for multi-application or multi-tenant brokers. One vhost per application, each app’s user granted access only to its own:

Terminal window
rabbitmqctl add_vhost orders
rabbitmqctl add_vhost billing
rabbitmqctl set_permissions -p orders orders_app '.*' '.*' '.*'
# billing_app has no permissions on orders at all

Running everything in the default / vhost means every credential shares one namespace, and permission regexes are the only separation — which works, but is more error-prone than a hard vhost boundary.

The three permissions are not read/write/admin

Section titled “The three permissions are not read/write/admin”

This is the part people get wrong, because the names don’t mean what they look like. RabbitMQ permissions are three regular expressions, matched against resource names (queues and exchanges):

Permission Grants the right to… On resources matching
configure create and delete queues/exchanges, set policies the configure regex
write publish to an exchange, bind a queue the write regex
read consume from a queue, get messages the read regex

So set_permissions -p / app '.*' '.*' '.*' grants configure, write and read on everything — the .* is a name pattern, not an access level. To let an app publish and consume its own queues but not create or delete anything:

Terminal window
rabbitmqctl set_permissions -p orders orders_app '^$' '^orders\.' '^orders\.'

'^$' for configure matches nothing — the app cannot create or delete queues, which is usually correct: queues are created at deploy time by a privileged account, and the runtime app only publishes and consumes. That separation is the RabbitMQ equivalent of splitting DDL from runtime in Postgres.

Tags control management access, not messaging:

Tag Grants
administrator full management, all vhosts, user management
monitoring read-only management across all vhosts
management management UI for own vhosts only
(none) AMQP only — no management UI at all

An application user should have no tag — it needs to publish and consume, not log into the management UI. Giving an app the administrator tag so it “just works” hands it the whole broker’s management surface on top of its messaging.