From 20d59edfcd1adcfc0821c63a66d1b71b35d179f6 Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Wed, 8 Jul 2026 08:31:34 +0200 Subject: [PATCH] mosquitto: tighten ACLs to least-privilege for shellies + sml2mqtt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security review finding #sec-3/#sec-4. - shellies: `readwrite homeassistant/#` -> `write homeassistant/#`. Shelly Gen2 firmwares self-publish HA MQTT-discovery configs (so the grant must stay), but a publish-only client never needs to READ the whole discovery/state tree. Write-only removes device-inventory recon for a compromised Shelly while keeping discovery working. (`readwrite shellies/#` stays — devices publish state and subscribe to their command topics.) - sml2mqtt: `readwrite sml2mqtt/#` -> `write sml2mqtt/#`. The bridge is publish-only (verified: its config has no subscribe/command topics, only LWT + value publishes), so it needs write only. Left as-is deliberately: open3e keeps `readwrite open3e/#` (it subscribes to open3e/cmnd in listener mode); homeassistant/admin keep full access; evcc is already least-privilege. Per-device Shelly credentials were considered and rejected — Mosquitto's static passwd/acl model can't scope them without a dynamic-auth plugin, which isn't worth it at this device count. Applying rolls the (single-replica, Recreate) broker -> a brief MQTT reconnect. kustomize build clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../home-assistant/mosquitto-deployment.yaml | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/kubernetes/apps/home-assistant/mosquitto-deployment.yaml b/kubernetes/apps/home-assistant/mosquitto-deployment.yaml index e691c42e..88c4a0b4 100644 --- a/kubernetes/apps/home-assistant/mosquitto-deployment.yaml +++ b/kubernetes/apps/home-assistant/mosquitto-deployment.yaml @@ -44,16 +44,21 @@ spec: mosquitto_passwd -b /mosquitto/auth/passwd open3e "$OPEN3E_PASSWORD" mosquitto_passwd -b /mosquitto/auth/passwd evcc "$EVCC_PASSWORD" # ACL: HA keeps full access (subscribes to everything); the - # shellies user is scoped to `shellies/#` for device state + - # `homeassistant/#` so HA MQTT-discovery payloads from Shelly - # Gen2 firmwares are accepted. Tighten the discovery scope - # later if devices only publish under specific subtrees. + # shellies user is `readwrite shellies/#` (devices publish state + # and subscribe to their command topics) + `write homeassistant/#` + # so Shelly Gen2 firmwares can publish HA MQTT-discovery configs. + # It is WRITE-only on homeassistant/# on purpose: a publish-only + # client never needs to read the whole discovery/state tree, and + # denying read removes device-inventory recon for a compromised + # Shelly. (Full per-device write scoping isn't expressible with + # the flat discovery topic layout; that would need a dynamic-auth + # plugin — out of scope.) # `admin` is a workstation/debugging credential — full # readwrite, kept on its own credential so its password can # be rotated independently of the HA session. - # `sml2mqtt` is the power-meter bridge — publish-only under - # its own `sml2mqtt/#` subtree (HA reads it via the full-access - # homeassistant user). + # `sml2mqtt` is the power-meter bridge — WRITE-only under its own + # `sml2mqtt/#` subtree (it only publishes; HA reads it via the + # full-access homeassistant user). # `open3e` is the Viessmann E3 CAN bridge (Vitocal/VX3) — # scoped to its own `open3e/#` subtree: publishes datapoints # and later subscribes to `open3e/cmnd` in listener mode (HA @@ -70,13 +75,13 @@ spec: user shellies topic readwrite shellies/# - topic readwrite homeassistant/# + topic write homeassistant/# user admin topic readwrite # user sml2mqtt - topic readwrite sml2mqtt/# + topic write sml2mqtt/# user open3e topic readwrite open3e/#