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
guestuser is never created. First boot generates two logins unique to your instance.sac-adminis the administrator. It works the console and HTTP API, and it can use AMQP only from the instance itself.sac-appis an application user limited to the virtual hostsac-appand to queues and exchanges whose names start withapp.. - 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 andguestlogins) 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.1only. 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.
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.
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.
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 socketThe services do the following:
lavinmq-metadata-namesreads this instance's EC2 names for the certificate.lavinmq-firstbootprepares the logins and the certificate.lavinmqis the broker. It serves AMQPS on 5671 for everyone, and its console backend on127.0.0.1:15672fornginxonly.- nginx serves the console on 443.
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.txtLAVINMQ_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=286c5846075aa06fbd14e3b2248d999851645c70c587d0d6059c629cd3291f91The 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.
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 -sha256CA: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 -sha256Compare 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.
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 / || trueOPEN 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.
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.passwordThis 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 pikaFor <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.
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/overview200Each 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-svc201 201 201The 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 || trueROUNDTRIP 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.passwordThe broker enforces these rules whatever you change:
- No user may be called
guest. - No user may have the password
guestor an empty password. sac-admincan 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).
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 1lavinmq-config-gate: OK (unit) gate-ok SELFCHECK OKSuppose 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.
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.1for nginx only. Usehttps://<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.
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")"; donelavinmq-selfcheck proves the block from inside the broker's own control group, where a request to the metadata service gets no answer.
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, usejournalctl -u nginxand/var/log/nginx/. For first boot, usejournalctl -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
lavinmqctlwrapper talks to the broker over its control socket, for examplesudo lavinmqctl list_users,sudo lavinmqctl list_vhostsandsudo lavinmqctl list_queues -p sac-app. - Sandbox: the broker runs as the unprivileged
lavinmquser 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