private@homelab: ~/latest
Home Assistant · Zigbee2MQTT · local control
private@homelab:~$ cat guides/article.md
· ·
9–13 minutes
read

Zigbee2MQTT MQTT Broker Security: A Practical Checklist

Secure Zigbee2MQTT and Mosquitto with current authentication, topic permissions, TLS, frontend protection and careful network configuration.

Zigbee2MQTT uses an MQTT broker for device data and control. If an untrusted client can publish control messages, it may be able to operate devices or change Zigbee2MQTT settings. Keeping that traffic on your LAN helps, but the broker, web frontend and host still need access controls.

This checklist covers the official Home Assistant Mosquitto app, formerly called an add-on, and standalone Mosquitto. Their configuration is different. Work through the relevant route, keep a copy of your existing configuration, and check legitimate clients after each change. The same broker may also serve automations involving groups and scenes or devices discussed in the ghost-device guide, so avoid treating it as a disposable part of the setup.

Local control still needs access control

A local MQTT connection does not require a vendor server to relay each command. It does not establish who can connect, what topics they can use, or whether traffic is encrypted. A client allowed to publish to a Zigbee2MQTT device’s /set topic can send supported commands; access to bridge request topics can expose wider administrative operations. The exact effect depends on the device and the permissions granted.

Keep that distinction when considering Xiaomi integration traffic or Tuya app connections too. Local execution and access control answer different questions. A compromised LAN device, leaked credentials or an unintended published port can still provide access.

Check which Mosquitto installation you use

The current official Home Assistant Mosquitto app, version 7.1.2 at this review, does not support anonymous logins. Its documentation says clients must authenticate. It accepts Home Assistant users and optional local users configured under logins. It does not have the old anonymous or plain configuration options.

Standalone Mosquitto uses its own mosquitto.conf and listener settings. The current manual documents allow_anonymous as false by default when listeners are configured; the no-listener default allows only local-machine connections. Older versions and custom configurations can behave differently. Inspect the active listener, authentication and topic permissions rather than copying an old add-on configuration into a current install.

Authentication and encryption are separate

A broker can require credentials while still accepting unencrypted MQTT. A username and password on port 1883 are not protected from someone who can observe that connection. Check the listener used by each client, not just whether login succeeds.

Broker credentials, topic permissions and TLS

For the official Home Assistant app, create a dedicated MQTT user through Home Assistant’s user management, or add a local app user. The names homeassistant and addons are reserved. This partial app configuration demonstrates the local-user option; replace the example password before use.

Mosquitto app configuration — partial local-user option
logins:
  - username: z2m_broker
    password: "REPLACE_WITH_A_LONG_UNIQUE_PASSWORD"

Do not use Home Assistant’s !secret tag in this app-options example. The app’s documented schema expects a password value. Protect the app configuration and backups that contain it. An empty logins: [] does not enable anonymous clients: Home Assistant user authentication remains available.

For standalone Mosquitto, configure an authenticated listener using the version’s supported password or authentication-plugin method and disable anonymous access explicitly if your configuration permits it. Create password-file entries with Mosquitto’s password tool rather than writing plaintext into that file. Restrict the listener to the intended interfaces and firewall scope. These are standalone broker settings, not fields to add to the official app’s options.

Use a separate account for each client where practical, then grant only the topics it needs through appropriate ACLs or an authorization plugin. Authentication alone need not restrict a logged-in client to safe topics. Zigbee2MQTT needs its own publish/subscribe topics; Home Assistant discovery and birth messages can require additional access. The app documentation describes custom ACLs and warns that its reserved internal users need appropriate unrestricted access. Test those dependencies before enforcing a narrower policy.

Set the same MQTT credentials in Zigbee2MQTT. The supported secrets-file syntax is a quoted string naming the file and key; it differs from Home Assistant’s YAML !secret tag.

Zigbee2MQTT configuration.yaml — MQTT with TLS
mqtt:
  server: 'mqtts://mqtt.home.example:8883'
  user: '!secret.yaml mqtt_user'
  password: '!secret.yaml mqtt_password'
  ca: '/app/data/mqtt-ca.crt'
  reject_unauthorized: true

Use your real broker hostname, listener and CA path, visible from the Zigbee2MQTT process. Put mqtt_user and mqtt_password entries in secret.yaml beside its configuration file. The example assumes your own CA certificate at the specified path; a certificate already trusted by the runtime may not require an explicit ca. The certificate must cover the name used by the client.

For the official Mosquitto app, supply its documented certificate and private-key files to enable the TLS listeners. Their presence also leaves the plaintext listeners active internally. The app documentation says to clear the 1883 and 1884 mappings in its Network card to stop exposing those ports on the host; that does not itself disable internal listeners or force internal app clients to use TLS. Use TLS-capable clients and the intended encrypted endpoint. Check 8884 too if you use MQTT over WebSockets.

Keep certificate verification enabled

reject_unauthorized: false disables Zigbee2MQTT’s server-certificate verification. Traffic may still be encrypted, but the client loses that check on the broker’s identity. Fix the certificate chain, hostname or trust configuration instead of using that bypass in production.

Protect the Zigbee2MQTT frontend

The frontend exposes Zigbee2MQTT’s control API over WebSockets. Its token authentication is disabled by default, according to the frontend configuration. Set a strong token and protect the connection with HTTPS/WSS, either directly or through a properly configured reverse proxy. Encryption and authentication are complementary.

Zigbee2MQTT configuration.yaml — standalone service behind a same-host proxy
frontend:
  enabled: true
  host: 127.0.0.1
  port: 8080
  auth_token: '!secret.yaml z2m_frontend_token' 

Add z2m_frontend_token to secret.yaml. This loopback example applies when the proxy and Zigbee2MQTT share the host network namespace. In a bridged Docker container, 127.0.0.1 means that container itself. Binding there can prevent a proxy on the NAS host or in another container from reaching it. Instead, choose the container network and published host address deliberately; a host mapping such as 127.0.0.1:8080:8080 restricts the published port to host loopback while the service listens on a reachable container interface.

The Home Assistant Zigbee2MQTT app uses Ingress and forces frontend port 8099. Follow its integration requirements rather than applying the standalone loopback/port example blindly. Audit any separately published frontend port: the authenticated Home Assistant sidebar is not proof that a direct port has identical protection. For remote administration, a trusted VPN or authenticated HTTPS proxy can provide access without forwarding the raw frontend port publicly.

Protect the Zigbee network key and joining process

A new network should use a random Zigbee network key. Current onboarding can generate one; an established network that already has a private random key does not need routine regeneration merely because the key is old. Plan replacement if it is exposed or if the network still uses the known historical default.

configuration.yaml — new network or deliberate key replacement
advanced:
  network_key: GENERATE

Zigbee2MQTT replaces GENERATE with a random key on startup. Changing an existing network’s key requires re-pairing its devices under the documented procedure. Preserve configuration and backups first, and plan that disruption rather than treating this as a harmless toggle. PAN IDs and device IEEE addresses identify a network or device; they are not secret substitutes for the key.

Keep joining closed outside planned pairing windows. Where both the device and adapter support install-code joining, it can protect the initial transport of the network key. A passlist can help reject unintended devices, but it works through device identity and leave requests; a malicious device can ignore a request. It is not a cryptographic replacement for protecting the key and commissioning process.

Touchlink is separate inter-PAN commissioning, used by some devices for close-range identification or factory resets. There is no general Zigbee2MQTT setting that disables another device’s own Touchlink behavior. Avoid devices that leave it permanently available in accessible locations, and follow model-specific pairing guidance. Proximity reduces the opportunity for abuse; it is not an authentication credential.

Restrict files and service privileges

On a standalone installation, run Zigbee2MQTT as a dedicated unprivileged user, allow that user the required serial-device access, and restrict its complete data directory. Configuration and secrets files can contain broker credentials, the frontend token and the network key; backups and downloaded diagnostic data deserve the same care.

Terminal — owned configuration files on a standalone Linux install
chmod 600 configuration.yaml secret.yaml

Run that command from the actual data directory, after confirming that the Zigbee2MQTT service user owns the files and that secret.yaml exists. Mode 600 grants access to the owner only; it does not change ownership or keep the service working if another account owns the files. The service also needs suitable directory permissions to read, create and replace its data files.

For containers, match the mounted files’ ownership to the intended service user and serial-device group. For Home Assistant apps, use the supported container and storage controls; do not assume the app automatically guarantees non-root operation or mode 600 on every exported configuration file. Limit file-sharing access and protect Home Assistant backups as well.

Check port mappings, firewalls and router exposure

Inspect the whole route to the broker and frontend: service bind addresses, container port publishing, host firewall, VLAN rules and router forwarding. Check IPv6 firewall rules too, where direct addressing may exist without an IPv4-style port forward. Credentials and TLS are useful layers, but an unnecessary public listener adds exposure.

Router checks

Look for forwards or automatic mappings reaching your broker’s MQTT and WebSocket ports, commonly 1883, 8883, 1884 and 8884, and any custom frontend port. UPnP can allow clients to request mappings on a router that supports it. Its presence does not prove Mosquitto created one; inspect the actual entries and disable unneeded mapping services.

A dedicated IoT VLAN can limit which devices reach the broker if the inter-VLAN firewall rules enforce that policy. Putting everything on a separate subnet without restrictions does not create isolation. Allow the flows Home Assistant and Zigbee2MQTT need, then test from both an authorized client and a network that should be blocked.

Checklist

Component What to check Action
Official Mosquitto app Authenticated users and published listeners; current app rejects anonymous logins Use Home Assistant or local users; remove unneeded host port mappings
Standalone broker Active listeners, authentication and topic authorization Disable anonymous access where applicable; use credentials and appropriate ACLs
MQTT transport Whether each client uses TLS and validates the broker certificate Configure the trusted certificate/hostname; keep verification enabled
Zigbee2MQTT frontend Token, HTTPS/WSS and any direct port outside Ingress Authenticate access and restrict reachable interfaces and networks
Zigbee commissioning Private random key, joining windows and device capabilities Keep the existing private key; plan re-pairing if replacing an exposed/default key
Files and host Service privileges, ownership, data directory and backups Restrict access without breaking required writes or serial permissions
Network edge Published ports, IPv4/IPv6 firewall rules, forwards and automatic mappings Remove unnecessary exposure and test intended restrictions

FAQ

Is MQTT encrypted by default? A connection using mqtt:// is not TLS. Configure an encrypted broker listener and use mqtts:// with certificate verification. A password alone does not encrypt the connection.

Does the official Home Assistant Mosquitto app allow anonymous clients? Its current documentation explicitly says no. Optional local logins supplement Home Assistant users; they are not required to switch anonymous access off.

Can a broker user control every device? That depends on topic permissions. A client allowed to publish the relevant Zigbee2MQTT commands can control the exposed functions. Separate users and topic authorization reduce what one leaked account can do.

Do I need credentials on a private LAN? Yes, unless another carefully designed authentication mechanism applies. LAN membership alone does not establish trust, and a future device or network change can broaden access.

What if the Zigbee network key leaks? Treat it as compromised: a nearby attacker with suitable radio equipment and captured traffic may be able to decrypt network traffic and attempt attacks. Plan a new private key and device re-pairing; also remove the source of the leak and protect old backups.

Sources and final checks

The settings are checked against the linked current Mosquitto app documentation and schema, Mosquitto manual, and Zigbee2MQTT security guide. The MQTT configuration reference documents the connection and secrets syntax. These are configuration examples, not the result of a hardware penetration test.

After restarting the affected services, confirm Zigbee2MQTT reconnects and normal commands and reporting work. Check that a client without valid credentials is rejected, and that a restricted account cannot use forbidden topics. Open the frontend in a fresh browser session to verify its authentication. Finally, confirm the intended firewall and port restrictions from the networks you actually use. Routine local control does not require a new cloud account for these changes; downloading updates or using optional remote-access services is a separate matter.

local-firstHome Assistantno-cloud