mqtt.user and mqtt.password secret references are expanded to plaintext in configuration.yaml on restart (network_key is preserved)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.7k
- Forks
- 2k
- Avg merge
- 18h 55m
- Merged PRs (30d)
- 35
Description
What happened?
When mqtt.user and mqtt.password are defined with the documented '!secret.yaml ' syntax, Zigbee2MQTT replaces them with their plaintext values in configuration.yaml on every restart.
The same mechanism works correctly for advanced.network_key, which keeps its reference across restarts. Only the mqtt section is affected.
Before restart (configuration.yaml):
yaml
mqtt:
base_topic: zigbee2mqtt
server: mqtt://core-mosquitto:1883
user: '!secret.yaml mqtt_user'
password: '!secret.yaml mqtt_password'
advanced:
network_key: '!secret.yaml network_key'
enable_external_js: false
After restart:
yaml
mqtt:
base_topic: zigbee2mqtt
server: mqtt://core-mosquitto:1883
user: my_actual_user # <- expanded
password: my_actual_password # <- expanded
advanced:
network_key: '!secret.yaml network_key' # <- preserved, as expected
enable_external_js: false # <- preserved
secret.yaml itself is left untouched and still holds the correct values.
Additional observation. If the key referenced from configuration.yaml does not exist in secret.yaml (e.g. '!secret.yaml password' while the file defines mqtt_password), Zigbee2MQTT creates the missing key in secret.yaml with the in-memory value, and expands the reference to plaintext in configuration.yaml. So the write-back path is partially aware of the secret file, yet still inlines the value in the main file.
This looks related to (and is the mirror image of) #26943, where secret.yaml gets overwritten with the reference strings themselves.
What did you expect to happen?
configuration.yaml should keep the '!secret.yaml ' references after a restart, as it does for advanced.network_key, and as described in the documentation:
- https://www.zigbee2mqtt.io/guide/configuration/mqtt.html#specifying-mqtt-server-user-password-and-network-key-in-a-different-file
- https://www.zigbee2mqtt.io/guide/installation/14_securing.html#configuration-file
The Securing the installation page explicitly recommends this mechanism to avoid storing credentials in plaintext, so the current behaviour defeats a documented security recommendation.
How to reproduce it (minimal and precise)
- Stop Zigbee2MQTT (important: editing while running gets overwritten from memory anyway).
- Create secret.yaml next to configuration.yaml:
yaml
mqtt_user: my_user
mqtt_password: my_password
network_key: [ ... your key ... ]
- Edit configuration.yaml:
yaml
mqtt:
user: '!secret.yaml mqtt_user'
password: '!secret.yaml mqtt_password'
advanced:
network_key: '!secret.yaml network_key'
- Start Zigbee2MQTT.
- Reopen configuration.yaml: mqtt.user and mqtt.password are in plaintext, advanced.network_key still holds its reference.
Zigbee2MQTT version
2.13.0-1 (Home Assistant add-on)
Adapter firmware version
Revision: 8.0.2 [GA]
Adapter
SMLIGHT SLZB-06Mg24 (EFR32MG24), ember driver, TCP connection
Setup
Home Assistant add-on on Home Assistant OS 18.2 (HA Green, aarch64), Home Assistant Core 2026.8.3.
Device database.db entry
No response
Debug log
Not applicable — the issue is reproducible purely from the configuration file contents before and after a restart.
Notes
this is a re-report of #27077 (opened 2025-04-11 against 2.2.1-1, auto-closed as stale without investigation). The behaviour is unchanged in 2.13.0-1. The new element compared to that report is that advanced.network_key is preserved while the mqtt section is not, which should help narrow down the write-back code path.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal restart reproduction in the issue and trace the configuration write-back path for mqtt.user and mqtt.password, comparing it with advanced.network_key handling. Done means the !secret.yaml references remain in configuration.yaml after restart, secret.yaml remains correct, and a regression test covers the existing-key case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100