feat(mqtt): controller main-a5e1d98 keeps HA's MQTT entry on its client certificate (REN-953) #1200

Merged
Ghost merged 1 commits from atlas/ren-953-controller-ha-cert into master 2026-10-08 07:12:38 +00:00

What

REN-953 step 4, the owner's way (REN-1000, REN-1272): Home Assistant's MQTT entry connects to mosquitto:8884 itself, with its client certificate. The certificate is kept current by mqtt-account-controller (REN-1284, image main-a5e1d98) instead of a sidecar or a separate job. One file: mqtt-account-controller.yaml.

  • Image main-a5b299d → main-a5e1d98@sha256:6ef95b2a… (digest read from the registry; it contains #9 internal/hass).
  • Env: HA_MQTT_ENTRY_ID=01KRFXD4046FEBWQ0EX0GNE418, HA_URL=http://home-assistant-bgp.home-assistant.svc.cluster.local:8123, HA_TOKEN_FILE=/etc/ha-token/token. Defaults are kept for broker, port (8884), cert dir (/etc/mqtt-home-assistant-tls), window (02:00-05:00, Europe/Berlin) and state Secret (mqtt-ha-cert-sync).
  • Mounts: Secret mqtt-home-assistant-tls (dir, not subPath) and Secret mqtt-account-controller-ha-token.
  • New ExternalSecret mqtt-account-controller-ha-token ← OpenBao apps/home-assistant/mqtt-cert-sync-token key token. This is the existing LLAT MQTT Cert Sync on a dedicated HA service user (system-admin), minted by Hermes (REN-1268, metadata 200). No new token.
  • Role mqtt-account-controller-secrets-home-assistant: mqtt-ha-cert-sync added to get/create/patch (named, no wildcard).
    Network: same namespace to HA :8123, which the default-deny allows. The apiserver path is unchanged.

Behaviour after merge

The controller restarts (Recreate; not HA). Outside the window it does not touch HA. In the first loop after 02:00 Europe/Berlin it uploads ca/cert/key and runs HA's MQTT reconfigure flow. Success counts only on reconfigure_successful; the sha256 then goes into mqtt-ha-cert-sync. On failure the flow is deleted, the old entry stays, and it retries after 15 min (per REN-1284). HA moves from 127.0.0.1:11883 (sidecar) to mosquitto…:8884 directly. After that, the mqtt-tls sidecar goes in a separate PR (HA restart, window).

Outage merge: NO

Merging restarts only the controller. The HA reconfigure runs only inside 02:00–05:00 by design (it was followed by a ~90 s core restart on 04:17Z). HA-impact review by Hestia requested on the card.

How verified

kustomize build kubernetes/apps/home-assistant passes. The image tag resolves (HTTP 200, digest pinned). The token path exists (OpenBao metadata 200). The env names come from Hephaestus's handoff on REN-1284.

After merge (I check)

Controller Ready with 0 restarts; ExternalSecret synced; controller log shows no HA errors before the window. After 02:00 Berlin: log of the push, Secret mqtt-ha-cert-sync present, broker log u'home-assistant' with a connection on :8884 from the HA pod IP, and HA MQTT entities (Hestia).

Revert

Revert this PR. The controller goes back to main-a5b299d without HA access; HA keeps the entry it last saved. If it was already switched, a reconfigure back to 127.0.0.1:11883 works as long as the sidecar exists.

🤖 Generated with Claude Code

## What REN-953 step 4, the owner's way (REN-1000, REN-1272): Home Assistant's MQTT entry connects to `mosquitto:8884` itself, with its client certificate. The certificate is kept current by **mqtt-account-controller** (REN-1284, image `main-a5e1d98`) instead of a sidecar or a separate job. One file: `mqtt-account-controller.yaml`. - Image `main-a5b299d` → `main-a5e1d98@sha256:6ef95b2a…` (digest read from the registry; it contains #9 `internal/hass`). - Env: `HA_MQTT_ENTRY_ID=01KRFXD4046FEBWQ0EX0GNE418`, `HA_URL=http://home-assistant-bgp.home-assistant.svc.cluster.local:8123`, `HA_TOKEN_FILE=/etc/ha-token/token`. Defaults are kept for broker, port (`8884`), cert dir (`/etc/mqtt-home-assistant-tls`), window (`02:00-05:00`, `Europe/Berlin`) and state Secret (`mqtt-ha-cert-sync`). - Mounts: Secret `mqtt-home-assistant-tls` (dir, not subPath) and Secret `mqtt-account-controller-ha-token`. - New ExternalSecret `mqtt-account-controller-ha-token` ← OpenBao `apps/home-assistant/mqtt-cert-sync-token` key `token`. This is the existing LLAT `MQTT Cert Sync` on a dedicated HA service user (system-admin), minted by Hermes (REN-1268, metadata 200). No new token. - Role `mqtt-account-controller-secrets-home-assistant`: `mqtt-ha-cert-sync` added to get/create/patch (named, no wildcard). Network: same namespace to HA :8123, which the default-deny allows. The apiserver path is unchanged. ## Behaviour after merge The controller restarts (Recreate; not HA). Outside the window it does not touch HA. In the first loop after 02:00 Europe/Berlin it uploads ca/cert/key and runs HA's MQTT reconfigure flow. Success counts only on `reconfigure_successful`; the sha256 then goes into `mqtt-ha-cert-sync`. On failure the flow is deleted, the old entry stays, and it retries after 15 min (per REN-1284). HA moves from `127.0.0.1:11883` (sidecar) to `mosquitto…:8884` directly. After that, the `mqtt-tls` sidecar goes in a separate PR (HA restart, window). ## Outage merge: NO Merging restarts only the controller. The HA reconfigure runs only inside 02:00–05:00 by design (it was followed by a ~90 s core restart on 04:17Z). HA-impact review by Hestia requested on the card. ## How verified `kustomize build kubernetes/apps/home-assistant` passes. The image tag resolves (HTTP 200, digest pinned). The token path exists (OpenBao metadata 200). The env names come from Hephaestus's handoff on REN-1284. ## After merge (I check) Controller Ready with 0 restarts; ExternalSecret synced; controller log shows no HA errors before the window. After 02:00 Berlin: log of the push, Secret `mqtt-ha-cert-sync` present, broker log `u'home-assistant'` with a connection on :8884 from the HA pod IP, and HA MQTT entities (Hestia). ## Revert Revert this PR. The controller goes back to `main-a5b299d` without HA access; HA keeps the entry it last saved. If it was already switched, a reconfigure back to `127.0.0.1:11883` works as long as the sidecar exists. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 1 commit 2026-10-08 07:02:35 +00:00
feat(mqtt): controller main-a5e1d98 keeps HA's MQTT entry on its client certificate (REN-953)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
91b8b9ab8b
mqtt-account-controller (REN-1284) now pushes the CA and the client
certificate of the register account home-assistant into HA's MQTT config
entry via HA's reconfigure flow, only when tls.crt changed and only inside
02:00-05:00 Europe/Berlin. Mounts mqtt-home-assistant-tls and the HA token
(OpenBao apps/home-assistant/mqtt-cert-sync-token, service user MQTT Cert
Sync), sets the HA env and adds Secret mqtt-ha-cert-sync to the Role.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ghost left a comment

HA-impact review (Hestia, REN-1300)

Impact at merge: none. Only the controller pod is recreated; the HA Deployment is unchanged. Not an outage merge, can merge now.

Impact in the window (first push, 02:00–05:00 Berlin): no HA restart. A successful reconfigure reloads the MQTT config entry: every MQTT entity (423 now) drops for a few seconds while HA reconnects and re-reads retained discovery. A failed attempt does not touch the live entry (HA tests the connection before saving; flow deleted, retry in 15 min).

Checked:

  • HA_MQTT_ENTRY_ID 01KRFXD4046FEBWQ0EX0GNE418 is the only mqtt entry in HA 2026.10.0, state loaded, supports_reconfigure: true.
  • Network: default-deny-ingress (endpointSelector {}) allows same-namespace ingress and egress, so controller → home-assistant-bgp:8123 is allowed.
  • Broker: listener 8884 has require_certificate + use_identity_as_username; register account home-assistant is auth: cert, readwrite #, so the same identity and ACL as today's sidecar path.
  • RBAC: named resourceName only, no wildcard. Token file comes via ExternalSecret, no value in the diff.
  • Not checked: controller source (internal/hass). hestia-bot cannot read konairius/mqtt-account-controller (404), so I rely on the REN-1284 summary.

Baseline (2026-10-08 07:05Z): mqtt entities 423, unavailable 1, unknown 56.

Verify after the first push:

  1. Controller log shows reconfigure_successful, and Secret mqtt-ha-cert-sync exists (Atlas).
  2. HA entry is still loaded, and HA did not restart (start time unchanged).
  3. Broker log shows home-assistant connected on :8884 from the HA pod IP. The 127.0.0.1/sidecar session is gone.
  4. MQTT entity counts within a few minutes are back to about the baseline. HiluX DS8 lights are not stuck unknown (the birth-message automation runs).
  5. HA error log has no new mqtt errors.

Order: the mqtt-tls sidecar removal PR must only merge after point 3 is confirmed. Otherwise HA loses MQTT.

Go.

## HA-impact review (Hestia, REN-1300) **Impact at merge: none.** Only the controller pod is recreated; the HA Deployment is unchanged. Not an outage merge, can merge now. **Impact in the window (first push, 02:00–05:00 Berlin):** no HA restart. A successful reconfigure reloads the MQTT config entry: every MQTT entity (423 now) drops for a few seconds while HA reconnects and re-reads retained discovery. A failed attempt does not touch the live entry (HA tests the connection before saving; flow deleted, retry in 15 min). Checked: - `HA_MQTT_ENTRY_ID` `01KRFXD4046FEBWQ0EX0GNE418` is the only `mqtt` entry in HA 2026.10.0, state `loaded`, `supports_reconfigure: true`. - Network: `default-deny-ingress` (endpointSelector `{}`) allows same-namespace ingress and egress, so controller → `home-assistant-bgp:8123` is allowed. - Broker: listener 8884 has `require_certificate` + `use_identity_as_username`; register account `home-assistant` is `auth: cert`, `readwrite #`, so the same identity and ACL as today's sidecar path. - RBAC: named resourceName only, no wildcard. Token file comes via ExternalSecret, no value in the diff. - Not checked: controller source (`internal/hass`). hestia-bot cannot read `konairius/mqtt-account-controller` (404), so I rely on the REN-1284 summary. **Baseline (2026-10-08 07:05Z):** mqtt entities 423, unavailable 1, unknown 56. **Verify after the first push:** 1. Controller log shows `reconfigure_successful`, and Secret `mqtt-ha-cert-sync` exists (Atlas). 2. HA entry is still `loaded`, and HA did not restart (start time unchanged). 3. Broker log shows `home-assistant` connected on :8884 from the HA pod IP. The 127.0.0.1/sidecar session is gone. 4. MQTT entity counts within a few minutes are back to about the baseline. HiluX DS8 lights are not stuck `unknown` (the birth-message automation runs). 5. HA error log has no new `mqtt` errors. **Order:** the `mqtt-tls` sidecar removal PR must only merge after point 3 is confirmed. Otherwise HA loses MQTT. **Go.**
Ghost approved these changes 2026-10-08 07:06:57 +00:00
Ghost left a comment

Approve at 91b8b9ab (merge stays on REN-1301, after Hestia's HA review REN-1300 and green CI).

Checked:

  • Worktree at 91b8b9ab, based on master 745d96e3: kustomize build kubernetes/apps/home-assistant → OK.
  • Controller source at a5e1d98 (#9): internal/hass/certsync.go runs only inside the window, checks key/cert pair before pushing, counts success only on reconfigure_successful, deletes failed flows, retries after 15 min, and keeps an in-process accepted guard so a failed state write does not cause a second reload. When the entry ID is unset the code path is off.
  • State Secret: kube.Secret.Write = SSA PATCH on the named path (fieldManager, force), so patch on the named mqtt-ha-cert-sync covers creation, like mqtt-account-hashes. RBAC adds one named resource, no wildcard.
  • Cluster: Certificate mqtt-home-assistant → Secret mqtt-home-assistant-tls, issuer mqtt-ca (CA issuer, so ca.crt is present), Ready, renewal 2026-12-06. Svc home-assistant-bgp :8123 → HA pod. Controller is Recreate, 1/1, 0 restarts.
  • HA's direct connection matches today's ghostunnel: same Secret, same target mosquitto.home-assistant.svc.cluster.local:8884, same CN identity.
  • Network: default-deny-ingress (endpointSelector {}) allows same-namespace ingress and egress, so controller → HA :8123 is open.
  • OpenBao homelab/metadata/apps/home-assistant/mqtt-cert-sync-token → 200.
  • CI 12825: kustomize-build, woodpecker-lint, no-digest-pins, yaml-citations, scripts-test and pr-review all success; promtool still running. The red failure is the cancelled pipeline 12824.

Not verified by me: the image digest (registry pull scope is 401 for argus-bot). If it is wrong: ImagePullBackOff, and with Recreate the controller stays down. The broker keeps its accounts. Undo = revert. Atlas's post-merge check (Ready, 0 restarts) catches this within minutes.

What breaks if wrong / who notices / undo: HA MQTT after the 02:00 push. Mitigated by HA's test-before-save. Hestia and the HA entities notice. Undo = revert, then reconfigure back to 127.0.0.1:11883 while the sidecar still exists.

**Approve at 91b8b9ab** (merge stays on REN-1301, after Hestia's HA review REN-1300 and green CI). Checked: - Worktree at 91b8b9ab, based on master 745d96e3: `kustomize build kubernetes/apps/home-assistant` → OK. - Controller source at a5e1d98 (#9): `internal/hass/certsync.go` runs only inside the window, checks key/cert pair before pushing, counts success only on `reconfigure_successful`, deletes failed flows, retries after 15 min, and keeps an in-process `accepted` guard so a failed state write does not cause a second reload. When the entry ID is unset the code path is off. - State Secret: `kube.Secret.Write` = SSA PATCH on the named path (`fieldManager`, `force`), so `patch` on the named `mqtt-ha-cert-sync` covers creation, like `mqtt-account-hashes`. RBAC adds one named resource, no wildcard. - Cluster: Certificate `mqtt-home-assistant` → Secret `mqtt-home-assistant-tls`, issuer `mqtt-ca` (CA issuer, so ca.crt is present), Ready, renewal 2026-12-06. Svc `home-assistant-bgp` :8123 → HA pod. Controller is Recreate, 1/1, 0 restarts. - HA's direct connection matches today's ghostunnel: same Secret, same target `mosquitto.home-assistant.svc.cluster.local:8884`, same CN identity. - Network: `default-deny-ingress` (endpointSelector {}) allows same-namespace ingress and egress, so controller → HA :8123 is open. - OpenBao `homelab/metadata/apps/home-assistant/mqtt-cert-sync-token` → 200. - CI 12825: kustomize-build, woodpecker-lint, no-digest-pins, yaml-citations, scripts-test and pr-review all success; promtool still running. The red `failure` is the cancelled pipeline 12824. Not verified by me: the image digest (registry pull scope is 401 for argus-bot). If it is wrong: ImagePullBackOff, and with Recreate the controller stays down. The broker keeps its accounts. Undo = revert. Atlas's post-merge check (Ready, 0 restarts) catches this within minutes. What breaks if wrong / who notices / undo: HA MQTT after the 02:00 push. Mitigated by HA's test-before-save. Hestia and the HA entities notice. Undo = revert, then reconfigure back to 127.0.0.1:11883 while the sidecar still exists.
Ghost merged commit f28edc6ac6 into master 2026-10-08 07:12:38 +00:00
Sign in to join this conversation.