Connect Shelly to RabbitMQ over MQTT mTLS
Summary
This solution securely connects a Shelly IP device to RabbitMQ via the MQTT plugin using mutual TLS (mTLS), no passwords, using certificate based mutual authentication. A single Bash script installs and configures RabbitMQ, generates a private CA, server and client certificates, and then presents a table of configuration values. After it finishes, the only manual step on the Shelly is to upload ca.crt
, client.crt
, and client.key
, copy those values into the MQTT settings, and save it. Supports RabbitMQ series selection (--rmq-series 3.13|4.0|4.1
, default 4.1) from Team RabbitMQ’s Ubuntu noble repos and forces TLS 1.2
only. On 4.x, the MQTT client_id
is enforced from the certificate SAN (URI)
while the authenticated username
still comes from the certificate CN
; on 3.13, SAN->client_id
binding isn’t available. You can control the connect host (and the server cert CN/SAN) via --connect-dns
/ --connect-ip
. The script exports device certs to /etc/mqtt-cert
and, by default, also issues a monitor cert (<client_id>-mon
) in /etc/mqtt-cert-monitor
with tarballs in /tmp/.
Result: encrypted, certificate-authenticated MQTT with least-privilege topic access.
Link to the script here.
Important: This script is for development/testing only, an example of what’s possible. Use with care and do not deploy as is to production.
Architecture
[Shelly device]
|
| MQTT over TLS (mTLS) - port 8883
v
[RabbitMQ MQTT listener] ---> [amq.topic exchange] ---> [queues/subscriptions]
| |
| ‘-- topic ACLs scoped to your MQTT prefix
|
‘-- Management UI (HTTPS, 15671)
Transport: Shelly talks to RabbitMQ on 8883 (MQTTS). TLS is required; clients must present a valid certificate (mTLS), it forces TLS 1.2 only.
Identity: The device’s certificate CN maps to a RabbitMQ user (no username/password in Shelly).
Client ID binding: Enforced from certificate SAN (URI) on RabbitMQ 4.x . Username still comes from CN; 3.13 doesn’t support
SAN→client_id
)Authorization: Topic ACLs restricted to your chosen MQTT prefix (plus read only shellies/command for compatibility)
Tenancy: MQTT doesn’t carry vhosts, so connections are routed to a dedicated vhost (e.g., /shelly) via mqtt.vhost.
Management: HTTPS UI on 15671
Plain ports: Disabled by default. You can temporarily keep 1883/15672 with --keep-plaintext during migration.
Quick reference
Concern | Decision |
---|---|
Port (data) | 8883 / TCP (MQTTS with mTLS) |
Port (admin) | 15671 / TCP (HTTPS Management UI) |
Identity | Cert CN -> RabbitMQ user |
Client ID | User provided in Shelly; enforced via cert SAN |
Vhost | /shelly (set via mqtt.vhost) |
Topics | Restricted to your MQTT prefix (both slash and dot forms) |
Secrets on device | Client key only (no passwords) |
Plaintext | Off by default; opt-in with --keep-plaintext |
TLS Version | TLS 1.2 only |
Prerequisites
Tested environment (time of writing):
Ubuntu 24.04 LTS (amd64)
,RabbitMQ 4.1
(installed via this script), Shelly firmware1.7.1
.Install: from Team RabbitMQ repos pinned to Ubuntu noble.
Access: Shell on the host with sudo privileges; outbound internet for apt repos.
Addressing: Stable broker
IP/DNS
(script can auto-detect, fixed is better).Time sync: NTP enabled (cert validity depends on correct time).
Optional:
mosquitto-clients
for quick end-to-end testing.
What the script does
Install RabbitMQ 4.1 + Erlang on Ubuntu 24.04
Uses Team RabbitMQ apt repos (the current recommended path) to get modern Erlang/RabbitMQ. https://www.rabbitmq.com/docs/install-debian
Configure via rabbitmq.conf (not rabbitmq-env.conf)
Runtime settings (listeners, TLS, plugins) live in
rabbitmq.conf
;rabbitmq-env.conf
is only for environment variables. https://www.rabbitmq.com/docs/configureEnable MQTT + Management plugins
Turns on native MQTT support and the HTTPS management/UI and enables
rabbitmq_auth_mechanism_ssl
. https://www.rabbitmq.com/docs/mqttEnable
detailed_queues_endpoint
feature flag(UI only detail for Queues).
Enforce mutual TLS (mTLS) for MQTT
Sets
ssl_options.verify = verify_peer
andssl_options.fail_if_no_peer_cert = true
so clients must present a valid cert; forces TLS 1.2 only. https://www.rabbitmq.com/docs/mqtt#tlsPasswordless cert login (CN → user)
Enables
mqtt.ssl_cert_login = true
andssl_cert_login_from = common_name
. The mapped user must exist in RabbitMQ (no password needed). https://www.rabbitmq.com/docs/mqtt#tls-certificate-authenticationBind MQTT client_id to the certificate SAN (URI)
On RabbitMQ 4.1, sets
mqtt.ssl_cert_client_id_from = subject_alternative_name
so the CONNECT client_id must match the cert SAN (prevents ID spoofing). https://www.rabbitmq.com/docs/mqtt#usage-of-client_id-extraction-from-client-certificates-for-authentication
This binds client_id
to the cert SAN (URI)
. Username
still comes from CN
; if you want username
from SAN
instead, you’d switch to ssl_cert_login_from=subject_alternative_name
and ssl_cert_login_san_type=uri
in the config.
Issue two client certs (same CN, different SANs)
device:
SAN=URI:<client_id>
monitor:
SAN=URI:<client_id>-mon
Lets you subscribe with the monitor cert without disconnecting the device (distinct client IDs).
Lock down listeners & ports
MQTTS:
8883
Management (HTTPS):
15671
Plain 1883/15672 off by default; optional during migration with --keep-plaintext. https://www.rabbitmq.com/docs/networking
Create tenancy & permissions
Routes MQTT sessions to
/shelly
(mqtt.vhost). https://www.rabbitmq.com/docs/vhostsApplies topic permissions so publish/subscribe is limited to your MQTT prefix (plus read-only shellies/command). https://www.rabbitmq.com/docs/man/rabbitmqctl.8#Access_control
Deletes the default guest user.
If
set_topic_permissions
isn’t available on your RabbitMQ, the script warns and leaves broader topic access until you upgrade.
Output you use on the Shelly
Writes an export folder with
ca.crt
,client.crt
,client.key
and prints a table of the exact values to copy into the Shelly MQTT settings.
Implementation
Run the script
./shelly-rmq-mqtt/setup-mqtt-mtls.sh \
--admin-user admin \
--admin-pass 'Sh3lly-i0T!' \
-C 'Shelly-Group' \
--client-id 'test-device' \
--vhost /shelly \
--mqtt-prefix 1pm-mini
Helpful flags (as needed): --admin-user/--admin-pass, --ip <broker_ip>, --keep-plaintext, --no-monitor-cert, --debug.
--connect-dns <name> (CN/SAN for the server cert and what clients dial)
--connect-ip <ip> (adds IP to server cert SAN)
--rmq-series 3.13|4.0|4.1 (default 4.1)
--force-regen (rotate CA/server/client certs even if present)
--monitor-client-id <id> (defaults to <client_id>-mon)
--tls-dir <dir> --export-dir <dir> --monitor-export-dir <dir>
--log-level debug|info|warn|error
--admin-user/--admin-pass, --ip <broker_ip> (alias: --server-ip), --keep-plaintext, --no-monitor-cert, --debug
The script will run and will go over each step and will show a success indicator and an error.

When it finishes, note the summary table at the bottom:
Broker IP/ports
Client ID and MQTT prefix
Paths to
ca.crt
,client.crt
,client.key
(export folder)Shelly config options
Management URL:
https://<broker_ip>:15671
Admin user and password (password is auto-generated unless you pass
--admin-pass
)Log file path:
/tmp/rabbitmq-mqtt-setup.log

Upload certificates to the Shelly
Open Shelly Web UI → Settings → TLS Configuration.
Upload the three files from the script’s export folder:
CA certificate: ca.crt
Client certificate: client.crt
Client private key: client.key
Upload each file individually: select the file, click Upload, and wait for the popup confirming success. Repeat for all three.

Configure Shelly MQTT
Open Shelly Web UI → Settings → MQTT.
Configure the values from the script:
Enable: Selected
User TLS: Selected
Use Client Certificate: Enabled
RPC Status update: Enabled
Generic Status Update: Enabled
Server: Value from the script
Client ID: Value from the script
MQTT Prefix: Value from the script
Click Save Settings and reboot the device
Once finished with the configuration, the device should look like this:

After the Shelly reboots, run the monitoring command printed by the script’s summary. It’s auto-generated from the values you provided and uses the monitor certificate (distinct client_id) so it won’t disconnect the device. You should see messages flowing from the Shelly to RabbitMQ.

You should see the Shelly connect and publish.

You can also confirm success in the Shelly’s web interface: the MQTT icon in the top-right shows two arrows when connected. If you see the arrows, the device is actively linked to RabbitMQ over MQTT/TLS.

Troubleshooting
Time sync - Make sure broker & Shelly clocks are correct (NTP). Skewed time breaks TLS.
Three files uploaded -
ca.crt
,client.crt
,client.key
(each uploaded individually, success popup).Client ID vs SAN - On RabbitMQ 4.1, the MQTT Client ID must match the cert SAN (URI). If they differ, connection is rejected.
On RabbitMQ 3.13, binding
client_id
from SAN is not supported; onlyCN->username
is used.
CN user exists - The cert CN must match an existing RabbitMQ user (no password needed with mTLS).
Ports - Use 8883 (MQTTS). 1883/15672 are off unless you ran with --keep-plaintext.
Firewall - If UFW is active, the script opens 8883 and 15671; otherwise it does not change firewall rules.
Prefix - Topics must sit under your MQTT prefix or RabbitMQ ACLs will block them.