The service receives a dedicated RabbitMQ user from Stronghold with permissions only on its own virtual host and on resources with a given name prefix. Stronghold creates the user through the RabbitMQ management HTTP API and deletes it on revocation or when the lease expires.

Goal

Configure issuing dynamic RabbitMQ credentials to the orders service: on the orders virtual host, the user gets configure, write, and read permissions only on queues and exchanges whose names start with orders., and lives for 1 hour with renewal up to 24 hours.

Prerequisites

To follow this example, you need:

  • Stronghold and a token with permissions to configure secrets engines and policies.
  • RabbitMQ with the rabbitmq_management plugin enabled; the management HTTP API is reachable from Stronghold.
  • An existing orders virtual host, created for example with rabbitmqctl add_vhost orders.
  • Access to rabbitmqctl for verification: with d8 k exec in a broker pod or on a RabbitMQ node.

Step 1. Prepare an account for Stronghold

Create a dedicated RabbitMQ user with the administrator tag. Stronghold uses it to create and delete users:

rabbitmqctl add_user stronghold-admin '<STRONGHOLD_ADMIN_PASSWORD>'
rabbitmqctl set_user_tags stronghold-admin administrator

<STRONGHOLD_ADMIN_PASSWORD> is the password of the stronghold-admin user.

Do not use this account for anything else.

Step 2. Configure the secrets engine

Enable the RabbitMQ secrets engine and connect it to the management HTTP API:

  1. Enable the RabbitMQ secrets engine:

    d8 stronghold secrets enable rabbitmq
  2. Configure the connection to the management HTTP API:

    d8 stronghold write rabbitmq/config/connection connection_uri="https://rabbitmq.example.com:15672" username="stronghold-admin" password="<STRONGHOLD_ADMIN_PASSWORD>"
  3. Set the lease parameters in seconds. They apply to all roles of this secrets engine:

    d8 stronghold write rabbitmq/config/lease ttl=3600 max_ttl=86400

    To use different lease durations for different services, mount separate instances of the secrets engine with the -path argument.

Step 3. Create a role

Create a role that grants permissions on the orders virtual host:

d8 stronghold write rabbitmq/roles/orders vhosts='{"orders":{"configure":"^orders\\..*","write":"^orders\\..*","read":"^orders\\..*"}}'
  • vhosts: a JSON map of virtual hosts and permissions. The configure, write, and read values are RabbitMQ regular expressions for queue and exchange names.
  • tags is not set, so the user has no access to the management web UI and HTTP API.

If the service publishes to a topic exchange, restrict the allowed routing keys with vhost_topics:

d8 stronghold write rabbitmq/roles/orders vhosts='{"orders":{"configure":"^orders\\..*","write":"^orders\\..*","read":"^orders\\..*"}}' vhost_topics='{"orders":{"orders.events":{"write":"^order\\.(created|paid)$","read":".*"}}}'

Step 4. Create a policy

Create a policy that allows reading credentials of the orders role:

d8 stronghold policy write orders-rabbitmq - <<'POLICY'
path "rabbitmq/creds/orders" {
  capabilities = ["read"]
}
POLICY

Assign the policy to the auth method role the service logs in with, for example a Kubernetes auth role as in Dynamic PostgreSQL credentials. Renewing and revoking your own leases is allowed by the built-in default policy.

Step 5. Get credentials

Request credentials of the orders role and manage their lease:

  1. Request credentials:

    d8 stronghold read rabbitmq/creds/orders

    Example output:

    Key                Value
    ---                -----
    lease_id           rabbitmq/creds/orders/I39Hu8XXOombof4wiK5bKMn9
    lease_duration     1h
    lease_renewable    true
    password           3yNDBikgQvrkx2VA2zhq5IdSM7IWk1RyMYJr
    username           token-39669250-3894-8032-c420-3d58483ebfc4
  2. Renew the lease before lease_duration expires:

    d8 stronghold lease renew rabbitmq/creds/orders/I39Hu8XXOombof4wiK5bKMn9
  3. When the credentials are no longer needed, revoke the lease. Stronghold deletes the user in RabbitMQ:

    d8 stronghold lease revoke rabbitmq/creds/orders/I39Hu8XXOombof4wiK5bKMn9

Step 6. Deliver credentials to the service

Stronghold Agent renews the lease and re-renders the file when it receives new credentials. Example template that builds an AMQP connection URI:

{{ with secret "rabbitmq/creds/orders" }}
AMQP_URL=amqps://{{ .Data.username }}:{{ .Data.password }}@rabbitmq.example.com:5671/orders
{{ end }}

The Agent sidecar configuration is in Delivering secrets to Kubernetes pods. When max_ttl is reached, the service receives a new user and the old one is deleted, so the service must reconnect to the broker with the new credentials.

Verification

Check the result:

  1. Request credentials and store the username and password:

    creds="$(d8 stronghold read -format=json rabbitmq/creds/orders)"
    MQ_USER="$(echo "$creds" | jq -r '.data.username')"
    MQ_PASSWORD="$(echo "$creds" | jq -r '.data.password')"
    LEASE_ID="$(echo "$creds" | jq -r '.lease_id')"
  2. Check that the user exists and can authenticate. The commands run rabbitmqctl in the rabbitmq-0 broker pod from your workstation, so the MQ_USER and MQ_PASSWORD variables of the current shell are passed to rabbitmqctl:

    d8 k -n rabbitmq exec rabbitmq-0 -- rabbitmqctl list_users
    d8 k -n rabbitmq exec rabbitmq-0 -- rabbitmqctl authenticate_user "$MQ_USER" "$MQ_PASSWORD"
  3. Check the user’s virtual host permissions:

    d8 k -n rabbitmq exec rabbitmq-0 -- rabbitmqctl list_user_permissions "$MQ_USER"

    The output must contain only the orders virtual host with the ^orders\..* regular expressions.

  4. Revoke the lease and make sure the user is deleted:

    d8 stronghold lease revoke "$LEASE_ID"
    d8 k -n rabbitmq exec rabbitmq-0 -- rabbitmqctl list_users

The commands assume that RabbitMQ runs in a DP cluster in the rabbitmq namespace. If RabbitMQ runs outside Kubernetes, run rabbitmqctl with the same arguments on a RabbitMQ node, and set the MQ_USER and MQ_PASSWORD variables on that node to the values from the first step.

Cleanup

Delete the objects created in this example:

  1. Revoke all credentials issued for the role:

    d8 stronghold lease revoke -prefix rabbitmq/creds/orders/
  2. Delete the role and the policy, and disable the secrets engine if needed:

    d8 stronghold delete rabbitmq/roles/orders
    d8 stronghold policy delete orders-rabbitmq
    d8 stronghold secrets disable rabbitmq
  3. Delete the Stronghold account in RabbitMQ: rabbitmqctl delete_user stronghold-admin.

Additional resources