Kannisto.org

Petri Kannisto's homepage – about software systems and research

Secure RabbitMQ broker in virtualized Ubuntu Server

24 Mar 2021 – Petri Kannisto

DISCLAIMER. This guide comes "as is". This means that the author does not have any liability. You must consider carefully before exposing any server to the public Internet, because this is always risky. It is up to you to estimate if you know what you are doing.

This guide provides you step-by-step instructions to install RabbitMQ in a virtualized Ubuntu Linux that:

I originally wrote this guide in 2017-2018 for Ubuntu Server 16.04.6 LTS. However, the content is likely still valid, although some sections have been updated without trying if they truly work.

Instead of installing RabbitMQ from the bottom, you could run a Docker container ( https://hub.docker.com/_/rabbitmq ). This is straigthforward if Docker is familiar to you. However, follow this guide if you want to set up your own server.

Prerequisites

This guide assumes that you know the following. If you don't, the Internet is full of guides anyway.

Furthermore, your server must have a domain name. Otherwise, you cannot get yourself a certificate.

Known shortcomings

This guide could improve at least the following.

Ubuntu setup

This guide assumes that you install RabbitMQ on top of Ubuntu Server following this blog post: Virtualized Ubuntu Server for Internet services. If your system differs from this, be ready to adapt.

Firewall

By default, RabbitMQ exposes port 5671 for encrypted client connections. Assuming you use UFW as the firewall, you can open this port as follows.

sudo ufw allow 5671

The risk of the above is that the connections may come from any IP address. Therefore, if you can, you should limit this to a subset of IPs. For instance:

sudo ufw allow proto tcp from 192.168.144.1/24 to any port 5671

Disable automatic updates for Erlang

This section assumes you have enabled the tool Unattended-Upgrades.

There's one automatic update you want to avoid, namely Erlang, which is the runtime of RabbitMQ. You want this because an update may handicap RabbitMQ.

To blacklist Erlang from automatic updates, you must edit a file. I believe the file exists by default:

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

If an entry called "Unattended-Upgrade::Package-Blacklist" is there, add the following line:

"erlang";

If the blacklist entry is missing, add it as follows:

Unattended-Upgrade::Package-Blacklist {
      "erlang";
};

Install RabbitMQ

This part has been updated from the previous installation, but it has not been tried with a fresh machine.

Add Erlang repository to Ubuntu software sources (execute this in a directory under /home):

wget https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i erlang-solutions_2.0_all.deb

Install/update Erlang:

sudo apt-get install erlang

Add RabbitMQ signing key as a trusted key:

curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo apt-key add -

Enable HTTPS transport with Apt:

sudo apt-get install apt-transport-https

Add RabbitMQ repository to Ubuntu software sources. This will create the file "bintray.rabbitmq.list". In deb command, "bionic" is for Ubuntu 20.04. For other versions, see https://www.rabbitmq.com/install-debian.html

echo "deb https://dl.bintray.com/rabbitmq/debian bionic main" | sudo tee /etc/apt/sources.list.d/bintray.rabbitmq.list

Install RabbitMQ server:

sudo apt-get install rabbitmq-server

Next, it is advisable to try that RabbitMQ works. Create a simple piece of software that both publishes messages and subscribes for messages. If you fail, the logs may help. See /var/log/rabbitmq.

Configure RabbitMQ users

Change username of "guest"

By default, RabbitMQ has a user called "guest". This has a default password "guest", which is nice for quick experiments but a risk in actual use.

To change the password (replace "PWD" with an actual password):

rabbitmqctl change_password guest PWD

Create RabbitMQ admin user

To manage RabbitMQ, you should have a dedicated admin user. This is user is different from the user accounts of Ubuntu and works only in the context of RabbitMQ. Furthermore, you aren't supposed to authenticate any RabbitMQ clients with this user.

This guide assumes you pick "localrmqadmin" as the username, because this is descriptive. Create the user as follows; replace "PWD" with a good password:

sudo rabbmitmqctl add_user localrmqadmin PWD

Set the permissions of user "localrmqadmin" to configure/read/write any RabbitMQ resource (this applies to at least queues and exchanges):

sudo rabbitmqctl set_permissions localrmqadmin ".*" ".*" ".*"

Set the user tag of "localrmqadmin" to "administrator":

sudo rabbitmqctl set_user_tags localrmqadmin administrator

Create client users and set permissions

Next, you need the user accounts that communicate via RabbitMQ. These can be as many as you want and have any permissions you want (or are able to set). However, these users are for communication instead of administration.

To create a user account (make sure to replace PWD with a strong password):

sudo rabbmitmqctl add_user exampleuser PWD

If you have multiple users, you may want to restrict their access. You could, for instance, specify that a user can only use a particular exchange or only exchanges that start with a certain prefix. Honestly, the permission management of RabbitMQ sucks, because you must use regular expressions and because each rule affects both exchanges and queues. However, it is doable.

With RabbitMQ, I have always used topic-based communication with randomly generated topic names. Because autogenerated queue names have the common "amq" prefix, the permissions work nicely if you allow a certain exchange name and the prefix.

For example, this allows any item that matches "myexample" or starts with "amq":

sudo rabbitmqctl set_permissions exampleuser myexample|^amq.* myexample|^amq.* myexample|^amq.*

In the example, the regex is repeated three times for different items: "configure", "write" and "read". Write and read are for messaging, but you need configure to create something. My use cases have always been happy with an identical value in each of the three.

Once you have created a rule, it's best to try if it works. Regexes are sometimes painful.

Once you have created all the beautiful regexes, you can view the result with:

sudo rabbitmqctl list_permissions

For reference, see: https://www.rabbitmq.com/rabbitmqctl.8.html

Install rabbitmqadmin tool

To enable more powerful administration, you should install the tool Rabbitmqadmin. This enables you to manage queues and exchanges. You could use the RabbitMQ browser interface instead, but the operating system has no graphical user interface and we don't want to expose the browser interface to the outside due to security risks.

In you home folder, download the "rabbitmqadmin" tool:

wget http://localhost:15672/cli/rabbitmqadmin

To make this tool accessible from any folder, move it as follows:

sudo mv rabbitmqadmin /usr/local/bin

To manage queues and exchanges with this tool, you use the RabbitMQ user "localrmqadmin" you created earlier. Example command (please note you must assign the password):

rabbitmqadmin -u localrmqadmin -p PWD delete queue name=my-fancy-queue

For more information, see https://www.rabbitmq.com/management-cli.html

Generate server certificate files

This guide does not go deep into certificates. However, you will have a private key file and a public certificate file among others. If you can retrieve a certificate file that belongs to a publicly verified chain, good for you. If you use a self-signed certificate instead, you may have to install this locally into each client to enable connections. In some environments, you can override the validation of certificates, which is not recommended though.

Please note that your server requires a domain name. If no domain exists, the client will likely not accept the certificate. This is because you cannot associate a certificate to an IP address.

To create a self-signed certificate, do the following.

First, create a directory to store your certificates. This guide assumes this is "/home/hostadmin/certs".

Create a private key for the certificate authority (CA), which is you. This is supposed to remain secret!

openssl genrsa -des3 -out rootca.key 4096

Create a certificate for the CA:

openssl req -x509 -new -nodes -key rootca.key -sha256 -days 1024 -out rootca.crt

Now that we have a fabricated certificate authority, we can generate a certificate for our server.

First, create a key for the server. This is supposed to remain secret!

openssl genrsa -out server.key 2048

Next, you generate a signing request for the server:

openssl req -new -key server.key -out server.csr

Finally, your CA generates you a certificate:

openssl x509 -req -in server.csr -CA rootca.crt -CAkey rootca.key -CAcreateserial -out server.crt -days 500 -sha256

Converting certificates

Certificates come in many formats. Sometimes, systems require a specific format, but luckily you can convert these.

For example, CRT to PEM:

openssl x509 -in cert.crt -out cert.pem -outform PEM

PEM to CER:

openssl x509 -inform PEM -in cert.pem -outform DER -out cert.cer

Configure RabbitMQ access

This section explains how to enable and enforce encrypted connections as well as prevent the remote usage of admin users.

Create/edit the following file:

sudo nano /etc/rabbitmq/rabbitmq.config

Use the following in the file content (you may want to use file transfer). Make sure the filepath to certificates and key is correct as well as the format of the certificates. The config file does, for instance:

[
  {ssl, [{versions, ['tlsv1.2']}]},
  {rabbit, [
    {loopback_users, [<<"guest">>, <<"localrmqadmin">>]},
    {tcp_listeners, []},
    {ssl_listeners, [5671]},
    {ssl_options, [
      {cacertfile,"/home/hostadmin/certs/rootca.pem"},
      {certfile,"/home/hostadmin/certs/server.pem"},
      {keyfile,"/home/hostadmin/certs/server.key"},
      {verify,verify_none},
      {fail_if_no_peer_cert,false},
      {versions, ['tlsv1.2']}
    ]}
  ]}
].

Next, re-start RabbitMQ to apply the configuration:

sudo service rabbitmq-server restart

Next, try with a client program if you can access RabbitMQ. If problems occur (which is probable), see RabbitMQ logs. These reside in /var/log/rabbitmq.