feat(mosquitto): add the open3e-int broker user for the internal-CAN gateway #952

Merged
k.renner merged 1 commits from feat/open3e-int-broker-user into master 2026-09-24 18:22:02 +00:00
Member

Adds broker user open3e-int for the open3e-esp32 gateway that will sit on the Vitocal 250-A internal CAN (ECU 0x6e0, room setpoints). ACL readwrite open3e/int/#, so its command topic open3e/int/cmnd stays outside edge-1's open3e/cmnd listener while staying inside the existing open3e/# namespace.

Mirrors the health credential pattern (PR for 91133c94): ExternalSecret from OpenBao, Class-A rotator CronJob (quarterly, months 3/6/9/12), per-secret policy + kubernetes auth role, Reloader annotation extended on the broker Deployment, env + passwd + ACL wiring.

Out-of-band OpenBao steps already done (2026-09-24, admin token): policy rotator-mosquitto-open3e-int written, auth role created, homelab/apps/home-assistant/mosquitto-open3e-int-credentials seeded with a 32-char alphanumeric password (version 1). After merge: ESO creates the Secret, Reloader rolls the broker, the user becomes valid. The device side (NVS image over USB) follows separately.

Rotation caveat, recorded in the rotator and policy headers: the consumer is an embedded device, so a rotation must be followed by updating the device's MQTT password.

Validated with kubectl kustomize on both kustomizations.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QNSJsCKwMip9y9YEZhDqxw

Adds broker user `open3e-int` for the open3e-esp32 gateway that will sit on the Vitocal 250-A internal CAN (ECU 0x6e0, room setpoints). ACL `readwrite open3e/int/#`, so its command topic `open3e/int/cmnd` stays outside edge-1's `open3e/cmnd` listener while staying inside the existing `open3e/#` namespace. Mirrors the `health` credential pattern (PR for 91133c94): ExternalSecret from OpenBao, Class-A rotator CronJob (quarterly, months 3/6/9/12), per-secret policy + kubernetes auth role, Reloader annotation extended on the broker Deployment, env + passwd + ACL wiring. Out-of-band OpenBao steps already done (2026-09-24, admin token): policy `rotator-mosquitto-open3e-int` written, auth role created, `homelab/apps/home-assistant/mosquitto-open3e-int-credentials` seeded with a 32-char alphanumeric `password` (version 1). After merge: ESO creates the Secret, Reloader rolls the broker, the user becomes valid. The device side (NVS image over USB) follows separately. Rotation caveat, recorded in the rotator and policy headers: the consumer is an embedded device, so a rotation must be followed by updating the device's MQTT password. Validated with `kubectl kustomize` on both kustomizations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QNSJsCKwMip9y9YEZhDqxw
claude-bot added 1 commit 2026-09-24 18:11:24 +00:00
feat(mosquitto): add the open3e-int broker user for the internal-CAN gateway
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
74b22853a9
The open3e-esp32 gateway on the Vitocal 250-A internal CAN (ECU 0x6e0,
room setpoints) publishes and listens under open3e/int/#. A separate
broker user keeps its command topic out of edge-1's open3e/cmnd listener
and gives the credential its own lifecycle.

Follows the health credential pattern: OpenBao-backed via ExternalSecret,
Class-A rotator CronJob (quarterly, months 3/6/9/12), per-secret policy
and kubernetes auth role, Reloader annotation on the broker.

Out-of-band steps done on 2026-09-24 with an admin token: policy written,
auth role created, path seeded with a 32-char alphanumeric password.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNSJsCKwMip9y9YEZhDqxw
review-bot left a comment
Member

🤖 Automated review — model CLI default (complexity: unknown), commit 74b22853a93e8bc46021d1950651b39cdc0007de.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

Adds the open3e-int mosquitto broker user (for the open3e-esp32 gateway on the Vitocal internal CAN), following the health credential pattern end-to-end: ExternalSecret, Class-A rotator CronJob, OpenBao policy/role doc, Reloader annotation, and passwd/ACL wiring. The mechanical mirroring is careful and consistent with the sibling health files (schedules don't collide, eso-read.hcl's homelab/data/* wildcard already covers the new path, no new NetworkPolicy is needed since the rotator stays intra-namespace to OpenBao — same as health). One real gap: the PR skips a charset check this repo's own docs say is required before enrolling a non-in-cluster consumer like this one.

🔴 Blocking

  • kubernetes/infrastructure/openbao/rotation/mosquitto-open3e-int.yaml:1-79 — the rotator uses secret-rotator random mode, which unconditionally produces standard base64 with padding (+, /, =) — confirmed in secret-rotator/src/secretgen.rs:8-26 (base64::engine::general_purpose::STANDARD, no alternate charset mode exists in main.rs). The consumer here is the open3e-esp32 embedded gateway, whose MQTT password is set through its web UI or an NVS image flashed over USB (per this file's own comment, mosquitto-open3e-int.yaml:149-151, and the PR body).

    The sibling mosquitto-health.yaml:24-31 explicitly documents this exact risk and treats it as a mandatory check: base64 output is safe for health specifically because it's only ever passed as a shell arg inside the container, and "That is NOT automatically true of the other seven users (sml2mqtt and open3e templates on the edge host, Shelly device firmware, the HA config entry), so the charset must be re-checked per user before enrolling each one — the old bootstrap Jobs generated alphanumeric-only passwords with tr -dc 'A-Za-z0-9'." open3e-int is squarely in that same risk class (arguably worse: embedded firmware/NVS, not a Linux edge host), yet neither mosquitto-open3e-int.yaml nor rotator-mosquitto-open3e-int.hcl records that the check was done, and the PR body only confirms the manually seeded value is alphanumeric — it says nothing about what happens once the CronJob fires on its own schedule (0 4 1 3,6,9,12 *, i.e. first run 2026-12-01).

    If the ESP32 firmware's MQTT client or the web UI/NVS tooling can't cleanly round-trip a password containing +, /, or = (untested here, and not verifiable by reading a firmware repo that isn't in this checkout), the very first automated rotation silently breaks the device's MQTT auth, and recovery needs physical USB access — a much higher-cost failure than any of the in-cluster rotators this pattern was proven against. Before merge, either confirm the device side actually accepts the full standard-base64 alphabet (and note that verification in the rotator/policy file, as health does for its own case), or don't enable unattended rotation for this credential until that's confirmed.

🟡 Suggestions

  • kubernetes/apps/home-assistant/PROPOSAL-mosquitto-credential-rotation.md:4 — scope is stated as "the 8 MQTT broker users"; open3e-int is a 9th, newly-created user that goes straight onto the target rotation pattern rather than migrating off a bootstrap Job. Worth a line noting it's additive/out-of-scope-of-the-migration, so a future reader doesn't wonder why the count doesn't match.

Tests

No automated tests apply to this class of change; the PR body states kubectl kustomize was run against both kustomizations, which is the established verification method for this repo's manifest changes. I did not re-run kubectl kustomize myself (sandboxed shell access wasn't available), but manual review of the diff against the mosquitto-health precedent found no YAML-structure discrepancies, and git diff context confirms both new files are correctly wired into their respective kustomization.yaml (kubernetes/apps/home-assistant/kustomization.yaml:19, kubernetes/infrastructure/openbao/rotation/kustomization.yaml:51).

Recommendation

Request changes — 1 blocking issue (unverified base64-charset compatibility with the embedded device before enabling unattended quarterly rotation).

🤖 **Automated review** — model `CLI default` (complexity: unknown), commit `74b22853a93e8bc46021d1950651b39cdc0007de`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary Adds the `open3e-int` mosquitto broker user (for the open3e-esp32 gateway on the Vitocal internal CAN), following the `health` credential pattern end-to-end: ExternalSecret, Class-A rotator CronJob, OpenBao policy/role doc, Reloader annotation, and passwd/ACL wiring. The mechanical mirroring is careful and consistent with the sibling `health` files (schedules don't collide, `eso-read.hcl`'s `homelab/data/*` wildcard already covers the new path, no new NetworkPolicy is needed since the rotator stays intra-namespace to OpenBao — same as `health`). One real gap: the PR skips a charset check this repo's own docs say is required before enrolling a non-in-cluster consumer like this one. ## 🔴 Blocking - `kubernetes/infrastructure/openbao/rotation/mosquitto-open3e-int.yaml:1-79` — the rotator uses `secret-rotator random` mode, which unconditionally produces **standard base64 with padding** (`+`, `/`, `=`) — confirmed in `secret-rotator/src/secretgen.rs:8-26` (`base64::engine::general_purpose::STANDARD`, no alternate charset mode exists in `main.rs`). The consumer here is the open3e-esp32 embedded gateway, whose MQTT password is set through its web UI or an NVS image flashed over USB (per this file's own comment, `mosquitto-open3e-int.yaml:149-151`, and the PR body). The sibling `mosquitto-health.yaml:24-31` explicitly documents this exact risk and treats it as a mandatory check: base64 output is safe for `health` *specifically* because it's only ever passed as a shell arg inside the container, and "That is NOT automatically true of the other seven users (sml2mqtt and open3e templates on the edge host, Shelly device firmware, the HA config entry), so the charset must be re-checked per user before enrolling each one — the old bootstrap Jobs generated alphanumeric-only passwords with `tr -dc 'A-Za-z0-9'`." `open3e-int` is squarely in that same risk class (arguably worse: embedded firmware/NVS, not a Linux edge host), yet neither `mosquitto-open3e-int.yaml` nor `rotator-mosquitto-open3e-int.hcl` records that the check was done, and the PR body only confirms the *manually seeded* value is alphanumeric — it says nothing about what happens once the CronJob fires on its own schedule (`0 4 1 3,6,9,12 *`, i.e. first run 2026-12-01). If the ESP32 firmware's MQTT client or the web UI/NVS tooling can't cleanly round-trip a password containing `+`, `/`, or `=` (untested here, and not verifiable by reading a firmware repo that isn't in this checkout), the very first automated rotation silently breaks the device's MQTT auth, and recovery needs physical USB access — a much higher-cost failure than any of the in-cluster rotators this pattern was proven against. Before merge, either confirm the device side actually accepts the full standard-base64 alphabet (and note that verification in the rotator/policy file, as `health` does for its own case), or don't enable unattended rotation for this credential until that's confirmed. ## 🟡 Suggestions - `kubernetes/apps/home-assistant/PROPOSAL-mosquitto-credential-rotation.md:4` — scope is stated as "the 8 MQTT broker users"; `open3e-int` is a 9th, newly-created user that goes straight onto the target rotation pattern rather than migrating off a bootstrap Job. Worth a line noting it's additive/out-of-scope-of-the-migration, so a future reader doesn't wonder why the count doesn't match. ## Tests No automated tests apply to this class of change; the PR body states `kubectl kustomize` was run against both kustomizations, which is the established verification method for this repo's manifest changes. I did not re-run `kubectl kustomize` myself (sandboxed shell access wasn't available), but manual review of the diff against the `mosquitto-health` precedent found no YAML-structure discrepancies, and `git diff` context confirms both new files are correctly wired into their respective `kustomization.yaml` (`kubernetes/apps/home-assistant/kustomization.yaml:19`, `kubernetes/infrastructure/openbao/rotation/kustomization.yaml:51`). ## Recommendation Request changes — 1 blocking issue (unverified base64-charset compatibility with the embedded device before enabling unattended quarterly rotation). <!-- pr-review:sha=74b22853a93e8bc46021d1950651b39cdc0007de -->
k.renner merged commit c3ce97236d into master 2026-09-24 18:22:02 +00:00
Sign in to join this conversation.