Deployment guide

LavinMQ message broker 2.10.4

Overview

LavinMQ is an open-source (Apache-2.0) message broker that speaks AMQP 0-9-1, the protocol RabbitMQ clients use. Producers publish messages to exchanges, the broker routes them into queues, and consumers read from the queues. Any AMQP 0-9-1 client library works with it, including pika (Python), amqplib (Node.js), the RabbitMQ Java and .NET clients and Bunny (Ruby).

This image runs LavinMQ 2.10.4 on Ubuntu 24.04 on Amazon EC2. It is one software version of the SecureAnyCloud streaming and messaging listing on AWS Marketplace, and you choose it when you subscribe.

What you get:

  • AMQP over TLS only. Clients on the network reach the broker on 5671 (AMQPS: TLS 1.3 or 1.2 with modern AEAD cipher suites only). Plain AMQP (5672), MQTT (1883 and 8883) and the plain HTTP console port are switched off in LavinMQ's own configuration, so nothing answers on them even if a security group allows them (Step 10).
  • A certificate made on your instance. At first boot the broker gets a TLS key and an end-entity certificate for this instance's own names and addresses. The certificate can vouch only for this broker and can never sign other certificates. Pin it in your clients (Step 5), or replace it with one from your own CA or Let's Encrypt (Step 12).
  • No default account. LavinMQ's built-in guest user is never created. First boot generates two logins unique to your instance. sac-admin is the administrator. It works the console and HTTP API, and it can use AMQP only from the instance itself. sac-app is an application user limited to the virtual host sac-app and to queues and exchanges whose names start with app..
  • A broker that refuses to run unsafely. Before every start, a configuration gate refuses an unsafe change (a plain-text listener, a console bound to the network, guest, a weak or missing password hash, clustering). After every start, a guard checks the running broker (users, listeners, anonymous and guest logins) and stops it if anything is wrong. If first boot cannot finish, the broker never serves.
  • The management console over HTTPS. The console listens on 127.0.0.1 only. nginx serves it on 443 with the same certificate, refuses requests for host names that are not this instance's, and blocks cross-site requests.
  • No telemetry, and no access to the instance metadata service. LavinMQ has no telemetry, and its metrics port is off. The broker, nginx and the boot service run with the EC2 instance metadata address blocked (Step 11).
  • A pinned supply chain. LavinMQ 2.10.4 comes from its vendor's signed packagecloud apt repository, with the repository key pinned by fingerprint and the package version held. Ubuntu 24.04 LTS is fully patched at build time.
  • The software on the image. LavinMQ 2.10.4 (the lavinmq package, with data in /var/lib/lavinmq and configuration in /etc/lavinmq/lavinmq.ini), nginx as the HTTPS front door of the console, and the SecureAnyCloud helpers lavinmq-probe (an AMQP test client), lavinmq-selfcheck, lavinmq-set-hostname, lavinmq-install-cert and a lavinmqctl wrapper.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route that you use for management).
  • A security group that allows the following inbound traffic:
    • Port 22 (TCP) from your admin network, for SSH.
    • Port 443 (TCP) from your admin network, for the management console and HTTP API over HTTPS.
    • Port 5671 (TCP) from the networks your applications connect from, for AMQP over TLS (AMQPS).

Nothing else needs to be open. The broker does not serve ports 5672, 15672, 1883 and 80, so opening them has no effect.

1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick LavinMQ Message Broker 2.10.4 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2. Select m5.large (or larger for many connections or high message rates), your key pair and a security group as above.

To launch with the AWS CLI instead, replace the values in angle brackets.

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=lavinmq-broker}]'

The image requires IMDSv2 (session tokens). First boot takes under a minute after the instance is running. The boot service reads the instance's EC2 names and creates the logins and the certificate. Then the broker starts and the guard checks it.

2

Connect to your instance

For LavinMQ Message Broker 2.10.4 on Ubuntu 24.04, the SSH user is ubuntu. Connect with your key pair.

ssh -i <your-key.pem> ubuntu@<public-ip>

The login banner shows where the logins and the certificate are.

3

Check the services and the listening set

Check that the services are running and that only the expected ports are listening.

systemctl is-active lavinmq-metadata-names lavinmq-firstboot lavinmq nginx
sudo ss -tlnp | grep -E ':(22|80|443|1883|5671|5672|8883|15672) '
sudo ss -ulnp | grep -E 'lavinmq|nginx' || echo 'no UDP socket'
active active active active LISTEN 0 128 127.0.0.1:15672 0.0.0.0:* users:(("lavinmq",pid=1070,fd=17)) LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1073,fd=5),("nginx",pid=1072,fd=5),("nginx",pid=1071,fd=5)) LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1144,fd=3),("systemd",pid=1,fd=57)) LISTEN 0 128 0.0.0.0:5671 0.0.0.0:* users:(("lavinmq",pid=1070,fd=16)) LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=1144,fd=4),("systemd",pid=1,fd=58)) no UDP socket

The services do the following:

  • lavinmq-metadata-names reads this instance's EC2 names for the certificate.
  • lavinmq-firstboot prepares the logins and the certificate.
  • lavinmq is the broker. It serves AMQPS on 5671 for everyone, and its console backend on 127.0.0.1:15672 for nginx only.
  • nginx serves the console on 443.
4

Your endpoint and logins

Everything generated for your instance is in /root/lavinmq-credentials.txt (readable by root only). View it all with sudo cat /root/lavinmq-credentials.txt. To print only the lines that are not secret, run the following command.

sudo grep -E '^LAVINMQ_(HOST|AMQPS_ENDPOINT|AMQPS_PORT|MGMT_URL|PRIVATE_IP|ADMIN_USER|APP_USER|APP_VHOST|APP_PREFIX|TLS_CERT_FILE|TLS_CERT_SHA256|TLS_PUBKEY_SHA256)=' /root/lavinmq-credentials.txt
LAVINMQ_HOST=44.212.4.95 LAVINMQ_AMQPS_ENDPOINT=amqps://44.212.4.95:5671 LAVINMQ_AMQPS_PORT=5671 LAVINMQ_MGMT_URL=https://44.212.4.95/ LAVINMQ_PRIVATE_IP=172.31.82.141 LAVINMQ_ADMIN_USER=sac-admin LAVINMQ_APP_USER=sac-app LAVINMQ_APP_VHOST=sac-app LAVINMQ_APP_PREFIX=app. LAVINMQ_TLS_CERT_FILE=/etc/lavinmq/tls/lavinmq.crt LAVINMQ_TLS_CERT_SHA256=8F:BB:1F:9D:6B:ED:3C:C3:87:4B:CE:5C:F2:9B:09:89:F2:AA:33:E3:18:89:DB:E4:60:20:66:FF:E3:77:E1:F6 LAVINMQ_TLS_PUBKEY_SHA256=286c5846075aa06fbd14e3b2248d999851645c70c587d0d6059c629cd3291f91

The file also holds LAVINMQ_ADMIN_PASSWORD and LAVINMQ_APP_PASSWORD. Give applications the sac-app login or a scoped user of their own (Step 8), never the administrator's.

Keep passwords in files with mode 0600, not in shell history or on command lines. Every user on a machine can see a command line through ps.

5

Check the certificate and pin it in your client

The broker's certificate is self-signed and made on your instance. Inspect it and read its fingerprint.

sudo openssl x509 -in /etc/lavinmq/tls/lavinmq.crt -noout -subject -enddate -ext subjectAltName,basicConstraints
sudo openssl x509 -in /etc/lavinmq/tls/lavinmq.crt -noout -fingerprint -sha256

CA:FALSE means it is an end-entity certificate that cannot sign anything else. The SHA-256 fingerprint is the LAVINMQ_TLS_CERT_SHA256 value in your credentials file. The same certificate is served on 5671 and on 443.

Copy the certificate to each client machine. The certificate is public, and the private key never leaves the instance.

client-host$ ssh -i <your-key.pem> ubuntu@<public-ip> 'sudo cat /etc/lavinmq/tls/lavinmq.crt' > lavinmq.crt client-host$ openssl x509 -in lavinmq.crt -noout -fingerprint -sha256

Compare the fingerprint with LAVINMQ_TLS_CERT_SHA256 before you trust the file. Then configure your client to trust only this file (its CA file or trust store), with host name checking on. Your client then refuses any other server, including a man-in-the-middle that presents a different certificate.

The certificate covers the instance's public and private IPv4 addresses and EC2 DNS names, localhost and 127.0.0.1. An EC2 stop and start gives the instance a new public IP. At the next boot, the certificate is re-issued for the new names with the same key. Clients that pin the public key (LAVINMQ_TLS_PUBKEY_SHA256) keep working. Clients that pin the certificate file need the new copy.

6

Test on the instance

lavinmq-probe is a small AMQP client. It reads passwords from a file, or from the credentials file with --identity, and it trusts only the broker's certificate. Run the following checks.

sudo lavinmq-probe connect --identity app
sudo lavinmq-probe roundtrip --identity app --queue app.guide-test
sudo lavinmq-probe declare --identity app --queue orders || true
sudo lavinmq-probe connect --identity app --vhost / || true
OPEN ok vhost=sac-app tls=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 ROUNDTRIP OK queue=app.guide-test bytes=55 tls=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 CHANNEL-CLOSE 403 ACCESS_REFUSED - User 'sac-app' doesn't have permissions to queue 'orders' REFUSED 530 NOT_ALLOWED - 'sac-app' doesn't have access to '/'

The application user logs in, declares a queue named app.guide-test, publishes a message and reads it back. The broker refuses a queue outside app.* and refuses the / virtual host. lavinmq-probe exits non-zero on a refusal, which is why the commands end with || true.

7

Connect a client from another machine

On the client machine, put the application password in a file that only you can read. Copy the certificate as in Step 5.

client-host$ umask 077 client-host$ ssh -i <your-key.pem> ubuntu@<public-ip> "sudo sed -n 's/^LAVINMQ_APP_PASSWORD=//p' /root/lavinmq-credentials.txt" > app.password

This example uses Python and pika. Install pika with sudo apt-get install python3-pika on Ubuntu, or with pip install pika.

# pika_demo.py - publish and consume through your sac LavinMQ broker over AMQPS (pika 1.x).
# Usage:
python3 pika_demo.py <broker-host> <lavinmq.crt> <user> <password-file> <vhost> <queue>
# The password is read from a file, never the command line; TLS trusts ONLY the broker's own certificate. import ssl import sys import pika host, cafile, user, pwfile, vhost, queue = sys.argv[1:7] with open(pwfile) as f: password = f.read().strip() ctx = ssl.create_default_context(cafile=cafile) # verify against this one certificate, nothing else ctx.minimum_version = ssl.TLSVersion.TLSv1_2 params = pika.ConnectionParameters( host=host, port=5671, virtual_host=vhost, credentials=pika.PlainCredentials(user, password), ssl_options=pika.SSLOptions(ctx, server_hostname=host), ) conn = pika.BlockingConnection(params) ch = conn.channel() ch.queue_declare(queue=queue, durable=True) ch.basic_publish(exchange="", routing_key=queue, body=b"hello from pika", properties=pika.BasicProperties(delivery_mode=2)) method, props, body = ch.basic_get(queue=queue, auto_ack=True) print("received:", body.decode()) conn.close()
client-host$ python3 pika_demo.py <public-ip> lavinmq.crt sac-app app.password sac-app app.pika-demo received: hello from pika

For <broker-host>, use a name the certificate covers. That is the public or private IP, an EC2 DNS name, or your own DNS name once you have added it (Step 12).

The AMQP URL form is amqps://sac-app:<password>@<public-ip>:5671/sac-app. Most libraries accept it. Build the URL inside your program from the password file, and do not write it in a configuration file or on a command line.

8

Manage over HTTPS with scoped users and virtual hosts

Open https://<public-ip>/ in a browser and sign in as sac-admin with LAVINMQ_ADMIN_PASSWORD. The certificate is self-signed, so the browser shows a warning until you trust lavinmq.crt or install a CA certificate (Step 12).

Give each application its own user, limited to its own virtual host and name prefix, rather than sharing one login. Create users in the console (Virtual hosts, then Users) or with the HTTP API as shown below.

Do not use lavinmqctl add_user or change_password. They take the password as a command-line argument, which other users on the instance can see with ps and which stays in your shell history.

On the instance, keep the administrator login in a curl configuration file that only you can read. Copy the certificate to a place where your user can read it.

umask 077
sudo cat /etc/lavinmq/tls/lavinmq.crt > ~/lavinmq.crt printf 'user = "sac-admin:%s"\n' "$(sudo sed -n 's/^LAVINMQ_ADMIN_PASSWORD=//p' /root/lavinmq-credentials.txt)" > ~/.lavinmq-admin.curl api() { curl -sS --cacert ~/lavinmq.crt -K ~/.lavinmq-admin.curl -H 'Content-Type: application/json' "$@"; } api -o /dev/null -w '%{http_code}\n' https://localhost/api/overview
200

Each block below defines the same small api helper, so you can run the blocks one at a time. This block creates a virtual host orders and a user orders-svc with a random password kept in a 0600 file. It limits the user's permissions to names starting with orders..

umask 077 api() { curl -sS --cacert ~/lavinmq.crt -K ~/.lavinmq-admin.curl -H 'Content-Type: application/json' "$@"; } api -o /dev/null -w '%{http_code}\n' -X PUT https://localhost/api/vhosts/orders openssl rand -base64 30 | tr -d '/+=\n' | head -c 32 > ~/orders-svc.password python3 -c 'import json,sys; print(json.dumps({"password": open(sys.argv[1]).read().strip(), "tags": ""}))' ~/orders-svc.password > ~/orders-svc.json api -o /dev/null -w '%{http_code}\n' -X PUT --data @"$HOME/orders-svc.json" https://localhost/api/users/orders-svc shred -u ~/orders-svc.json api -o /dev/null -w '%{http_code}\n' -X PUT --data '{"configure":"^orders\\.","write":"^(orders\\..*|amq\\.default)?$","read":"^orders\\."}' https://localhost/api/permissions/orders/orders-svc
201 201 201

The write pattern also allows amq.default, the default exchange, so the application can publish straight to its own queues by name. Test the new user.

sudo lavinmq-probe roundtrip --user orders-svc --password-file ~/orders-svc.password --vhost orders --queue orders.created
sudo lavinmq-probe declare --user orders-svc --password-file ~/orders-svc.password --vhost orders --queue app.x || true
sudo lavinmq-probe connect --user orders-svc --password-file ~/orders-svc.password --vhost sac-app || true
ROUNDTRIP OK queue=orders.created bytes=55 tls=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 CHANNEL-CLOSE 403 ACCESS_REFUSED - User 'orders-svc' doesn't have permissions to queue 'app.x' REFUSED 530 NOT_ALLOWED - 'orders-svc' doesn't have access to 'sac-app'

Hand ~/orders-svc.password to the application's secret store, then delete it with shred -u ~/orders-svc.password. To remove the example, run the following commands.

api() { curl -sS --cacert ~/lavinmq.crt -K ~/.lavinmq-admin.curl -H 'Content-Type: application/json' "$@"; } api -o /dev/null -w '%{http_code}\n' -X DELETE https://localhost/api/users/orders-svc api -o /dev/null -w '%{http_code}\n' -X DELETE https://localhost/api/vhosts/orders shred -u ~/orders-svc.password

The broker enforces these rules whatever you change:

  • No user may be called guest.
  • No user may have the password guest or an empty password.
  • sac-admin can use AMQP only from the instance itself (default_user_only_loopback).

From another machine, manage the broker over HTTPS on 443 with the console or the API (https://<public-ip>/api/... with --cacert lavinmq.crt).

9

Understand how the broker refuses to run unsafely

lavinmq.service runs lavinmq-config-gate before every start and lavinmq-guard after it. You can check both, and the whole instance, at any time.

sudo lavinmq-config-gate --unit && echo gate-ok
sudo lavinmq-selfcheck | tail -n 1
lavinmq-config-gate: OK (unit) gate-ok SELFCHECK OK

Suppose you edit /etc/lavinmq/lavinmq.ini and turn on a plain-text listener (amqp_port, mqtt_port or a non-loopback http_port), enable guest, or remove the per-instance administrator hash. The gate refuses the change and the broker does not start. journalctl -u lavinmq shows the reason.

If a user called guest, or a user whose password is guest, ever appears in the running broker, the guard stops it.

lavinmq-selfcheck prints one ok: line per area and ends with SELFCHECK OK. The areas are units, gate and guard, the listening set, the certificate, round trips, refusals, the console and the metadata block.

10

Why plain-text 5672, 15672 and 1883 are closed

By default, LavinMQ listens for plain AMQP on 5672, MQTT on 1883 and the HTTP console on 15672, all without TLS. It also creates guest / guest. On a cloud instance, that means clear-text passwords and messages and a well-known login.

This image switches those listeners off in LavinMQ's own configuration rather than relying on a firewall.

  • 5672 (AMQP): off. Use AMQPS on 5671. Every maintained AMQP 0-9-1 client supports TLS.
  • 15672 (HTTP console and API): bound to 127.0.0.1 for nginx only. Use https://<host>/ on 443.
  • 1883 / 8883 (MQTT): off. This image is configured as an AMQP broker.

From another machine, a connection to 5672, 15672, 1883, 8883, 15671, 15692 or 80 is refused even if your security group allows it.

11

Verify the instance metadata block

The EC2 instance metadata service (169.254.169.254) hands out the instance's IAM role credentials to any process that asks. A broker or web server that could be tricked into fetching a URL must not reach it.

lavinmq, nginx and lavinmq-firstboot run with the metadata addresses blocked by systemd (IPAddressDeny). Only lavinmq-metadata-names, which reads the instance's names for the certificate, may reach the metadata service, and it may reach nothing else. Check the settings with the following command.

for u in lavinmq nginx lavinmq-firstboot lavinmq-metadata-names; do printf '%-24s deny=%s allow=%s\n' "$u" "$(systemctl show -p IPAddressDeny --value "$u")" "$(systemctl show -p IPAddressAllow --value "$u")"; done

lavinmq-selfcheck proves the block from inside the broker's own control group, where a request to the metadata service gets no answer.

12

Use your own DNS name or a certificate from your CA

Point a DNS name at the instance. An Elastic IP keeps the address across stop and start. Then add the name to the certificate.

sudo lavinmq-set-hostname <dns-name>

This re-issues the self-signed certificate with the same key to cover the name, pins nginx to it, updates the endpoint in /root/lavinmq-credentials.txt and restarts the broker. Copy the new lavinmq.crt to clients that pin the certificate file.

To use a certificate from your CA, either your own or a public one such as Let's Encrypt, copy the chain (server certificate first) and its unencrypted PEM key to the instance. Then run the following command.

sudo lavinmq-install-cert <certificate-chain.pem> <private-key.pem> <dns-name>

Both endpoints (5671 and 443) then serve your certificate, and clients trust your CA instead of pinning this instance. The boot-time renewal never replaces an installed certificate. To go back to a new self-signed certificate, run sudo lavinmq-set-hostname <name>.

Logs, files and limits

  • Logs: the broker log is journalctl -u lavinmq. For the console front door, use journalctl -u nginx and /var/log/nginx/. For first boot, use journalctl -u lavinmq-firstboot.
  • Files: configuration is /etc/lavinmq/lavinmq.ini, which the gate checks on every start. Data is in /var/lib/lavinmq. The certificate and key are in /etc/lavinmq/tls/.
  • Control wrapper: the lavinmqctl wrapper talks to the broker over its control socket, for example sudo lavinmqctl list_users, sudo lavinmqctl list_vhosts and sudo lavinmqctl list_queues -p sac-app.
  • Sandbox: the broker runs as the unprivileged lavinmq user in a systemd sandbox (no capabilities, a read-only system, a system-call filter, core dumps off).
  • Storage: messages are kept on the instance's EBS volume. Size the volume and the instance for your message volume, and take EBS snapshots for backups.

Support

SecureAnyCloud supports the image, including launch problems, the first-boot setup, the TLS configuration and the SecureAnyCloud helpers. Contact sales@secureanycloud.com. LavinMQ itself is documented at lavinmq.com/documentation.

Deploy on AWS

Launch LavinMQ Message Broker 2.10.4 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS