Messaging

Fedora uses many event-driven services triggered by messages. In the past, this was done with ZeroMQ and fedmsg. This has been replaced by an AMQP message broker and the fedora-messaging for Python applications. This documentation outlines the policies for sending and receiving messages. To learn how to send and receive messages, see the fedora-messaging documentation.

Broker URLs

The broker consists of multiple RabbitMQ nodes. They are available through the proxies at amqps://rabbitmq.fedoraproject.org for production and amqps://rabbitmq.stg.fedoraproject.org for staging. Clients can connect using these URLs both inside and outside the Fedora VPN, but users outside need to use a separate virtual host. Consult the fedora-messaging documentation for details on how to connect externally.

Identity

In order to help with debugging, clients must configure the client_properties option to include their application name under the app key. Clients should include the application version, if possible, in the app_version key.

Authentication

When applications are deployed, clients must authenticate with the message broker over a TLS connection using x509 certificates. There are configuration options for this in fedora-messaging.

Clients require certificates issued by Fedora Infrastructure. If you’re not using the external, read-only user, file a ticket requesting a certificate for the AMQP broker and be sure to provide the username you plan to use. This is placed in the Common Name of the client certificate and must match the name of the user you create in AMQP. Consult the Authorization section below for details on creating users, queues, and bindings.

Authorization

The message broker can use virtual hosts to allow multiple applications to use the broker. The general purpose publish-subscribe virtual host is called /pubsub and has its authorization policy is outlined below. If your application is using a different virtual host for private messaging (for example, your application uses Celery), different authorization rules apply.

pubsub Virtual Host

AMQP clients do not have permission to create exchanges, queues, or bindings. However, they can and should declare the exchanges, queues, and bindings they expect to exist in their fedora-messaging configuration so that if they do not exist, the application will fail with a helpful error message about which resource is not available.

Warning

Because AMQP clients don’t have permission to create objects, you need to set passive_declares = true or you will get 403 Permission Denied errors.

Users, exchanges, queues, bindings, and virtual hosts other objects are managed in the broker using the Fedora Infrastructure Ansible project and must be declared there.

To do so, you can use the rabbit/queue role in the Ansible repository. An example usage in your deployment playbook would be:

roles:
  - role: rabbit/queue
    username: bodhi
    queue_name: bodhi_masher
    routing_keys:
      - "routing_key1"
      - "routing_key2"

Note that users only have permissions to read from queues prefixed with their name so if your username is “bodhi”, all queues must start with “bodhi”. The username must also match the common name in the x509 certificate used for authentication.

If you want to create a user that will only need to publish messages, and not consume them, you can use the rabbit/user role in the Ansible repository. An example usage in your deployment playbook would be:

roles:
  - role: rabbit/user
    username: bodhi

Please note that the username must match the TLS certificate’s Common Name, and they were created with the environment suffix. As a result, if you want the username to match in staging too, you should use:

username: "bodhi{{ env_suffix}}"

Bindings

Messages from AMQP publishers are sent to the amq.topic exchange. Messages from ZeroMQ publishers are sent to the zmq.topic exchange. In order to receive all messages during the transition period from ZeroMQ to AMQP, be sure to bind your consumers to both exchanges:

[[bindings]]
queue = "your queue"
exchange = "amq.topic"
routing_keys = ["key1", "key2"]

[[bindings]]
queue = "your queue"
exchange = "zmq.topic"
routing_keys = ["key1", "key2"]