MQTT Broker Hardening: TLS, Authentication and ACLs on Mosquitto
A step-by-step guide to securing a Mosquitto MQTT broker — disabling anonymous access, issuing TLS certificates, per-client authentication, and topic-level ACLs that actually restrict what devices can publish and subscribe to.
The Default Install Is Wide Open
A stock Mosquitto install on most distributions starts with a configuration that, if exposed, lets anyone publish and subscribe to anything. No username, no encryption, no topic restrictions.
That is a reasonable default for a five-minute lab test. It is a serious problem the moment the broker becomes reachable from anywhere untrusted — and MQTT brokers have a habit of quietly becoming reachable. Shodan consistently indexes tens of thousands of open MQTT brokers on port 1883, many of them publishing live industrial and building telemetry to whoever asks.
The failure is rarely "we chose not to secure it." It is that MQTT security has four distinct layers, and stopping after the first one feels like being done:
- Anonymous access — off
- Transport encryption — TLS
- Authentication — who is this client
- Authorisation — what may this client touch
Layer 4 is the one that gets skipped, and it is the one that limits blast radius when a device is compromised.
Related: IoT Real-Time Data Transfer Systems — the architecture this broker sits inside.
Layer 1: Stop Anonymous Access
By default, and with no listener configured, Mosquitto 2.x binds to localhost only — a deliberate improvement over 1.x. The moment you add a listener to expose it, you must also decide about anonymous clients.
Create the password file:
Two habits worth adopting now rather than later:
- One credential per device, not one shared "iot" account. A shared credential cannot be revoked without a fleet-wide outage, which in practice means it never gets revoked.
- Name credentials after the thing, so an ACL file and a log line are both readable six months later.
At this point credentials cross the network in cleartext. Fixing that is layer 2.
Layer 2: TLS
Generating certificates
For an internal deployment, your own CA is fine and avoids public-CA constraints on device certificates.
The CN on the broker certificate must match the hostname clients connect to. A mismatch is the single most common cause of "TLS just won't work" — clients that verify properly will reject it, and the temptation is then to disable verification, which throws away most of the benefit.
Broker configuration
Note the plain listener is pinned to 127.0.0.1. Local scripts and health checks keep working without exposing an unencrypted port. If you have no such need, delete the line entirely.
Verify from a client:
If that connects and a mosquitto_pub to test/hello arrives, transport and authentication are working.
Client certificates
For fixed industrial fleets, mutual TLS is stronger than passwords — a stolen password is portable, a device key is not.
use_identity_as_username true makes the certificate CN the username, so the same CN flows straight into your ACL rules and you maintain one identity list instead of two.
Layer 3 and 4: ACLs, Where the Real Work Is
Authentication proves identity. It does not stop an authenticated temperature sensor from subscribing to # and reading your entire plant, or publishing to a command topic that actuates a valve. That requires an ACL.
A worked example:
Read the direction keywords from the client's point of view: write means the client may publish; read means it may subscribe. Getting these backwards is the usual reason a correct-looking ACL blocks everything.
Use pattern rules for fleets
Enumerating a thousand gateways by hand is unmaintainable. pattern substitutes %u (username) and %c (client id):
Now sensor-gateway-01 can publish to telemetry/sensor-gateway-01/temp and nothing else — one rule covering the whole fleet, with per-device isolation.
Verify the denials, not the permissions
The mistake here is testing only the happy path. What matters is that the wrong things fail:
One caveat that costs people hours: a publish denied by ACL does not error at the client on MQTT 3.1.1 with QoS 0. The broker silently drops it. Check the broker log, or test with QoS 1, or use MQTT 5 which returns proper reason codes.
Operational Hardening
Then close the network path. The broker should not be internet-reachable unless it genuinely must be:
If remote devices need access across the internet, put them on a VPN and keep 8883 private rather than exposing the broker directly.
Related: Zero Trust for OT & Industrial Networks — segmentation principles that apply directly here.
Verification Checklist
[ ] allow_anonymous false, and confirmed anonymous connect is refused
[ ] Plain 1883 bound to loopback or firewalled off
[ ] TLS on 8883; broker cert CN matches the client's hostname
[ ] One credential per device, named after the device
[ ] ACL file present and acl_file set
[ ] Cross-device publish DENIED (verified, in broker log)
[ ] Wildcard '#' subscribe DENIED for device accounts (verified)
[ ] message_size_limit and max_queued_messages set
[ ] Broker not reachable from the public internet
[ ] Credential revocation tested — remove a user, confirm it cannot reconnect
The last item is the one nobody rehearses. Revoking a compromised device credential under time pressure, without taking down the fleet, is exactly the operation a per-device credential scheme exists to make possible — so prove it works before you need it.
Frequently Asked Questions
Is TLS enough to secure an MQTT broker?
No. TLS encrypts traffic in transit and lets the client verify the broker, but it says nothing about what an authenticated client is allowed to do. Without an ACL, any valid client can typically subscribe to # and read every topic on the broker. TLS plus per-device credentials plus topic ACLs is the minimum useful set.
What is the difference between MQTT ports 1883 and 8883?
1883 is the conventional plaintext MQTT port; 8883 is the conventional TLS port. Nothing enforces this — the security comes from your listener configuration, not the number. A TLS listener on 1883 is secure; a plaintext listener on 8883 is not.
Why is my MQTT publish silently failing?
Most often an ACL denial. On MQTT 3.1.1 with QoS 0 the broker drops a denied publish without telling the client, so the client sees success. Check the broker log for Denied PUBLISH, retry with QoS 1, or use MQTT 5, which returns explicit reason codes.
Should I use passwords or client certificates?
Client certificates for fixed device fleets — a certificate cannot be copied out of a config file and reused as easily as a password, and use_identity_as_username keeps identity management in one place. Passwords are reasonable for human-operated tools and short-lived integrations where certificate lifecycle management is more burden than benefit.
How do I stop one device reading another device's data?
Use pattern ACL rules scoped by %u, so each client is confined to a subtree named after its own username: pattern write telemetry/%u/#. Then verify the denial explicitly by trying to publish to another device's topic and confirming it is refused in the broker log.