Update: drop the API-key requirement from the update trigger
ci / build-test (push) Successful in 1m14s
ci / build-test (push) Successful in 1m14s
Owner's call: MeterVault__AllowInAppUpdate is now the whole gate. One click on the banner, no key, no prompt, and the REST endpoint no longer asks for one either. What that means, recorded so it is not rediscovered later: with the flag on, anything that can reach MeterVault can trigger a rebuild and restart. On the realistic threat model that is a repeatable denial of service — minutes of downtime and a pegged CPU per request — rather than code injection, because the build comes from the owner's own repository. It becomes remote code execution if that repository is ever compromised. The flag still defaults off, and that default is now the only thing between an upgrade and an open trigger, so UpdateRunnerTests pins it along with the fact that configuring API keys does not imply consent to rebuild the host. Kept one guard, which is not authentication: the REST endpoint requires an X-MeterVault-Update header. Without it any website could POST to the endpoint through the browser of someone on the network — a plain HTML form is enough, and no key means nothing else would stop it. A form cannot set a custom header and a cross-origin fetch that tries is stopped by a preflight nothing here answers, so this costs a deliberate caller one flag and costs the button nothing, since it runs over the Blazor circuit rather than HTTP. The confirmation dialog stays, now purely as a guard against a stray click costing several minutes of downtime. Every triggered update is logged as a warning: with no key there is no caller to attribute it to, and the restart discards anything held in memory. Claude-Session: https://claude.ai/code/session_01V6joyergfvVLFEizH1hJLd
This commit is contained in:
@@ -82,7 +82,7 @@ sources (Tasmota/HA/MQTT/manual/CSV)
|
||||
|
||||
**Time & DST (SDD §10):** store UTC everywhere; bucket and display in the instance timezone (default `Europe/Berlin`). "Daily cost" boundaries are local-midnight — use `time_bucket(..., 'Europe/Berlin')`.
|
||||
|
||||
**In-app update (`UpdateRunner`):** the dashboard shows a banner when a newer tag exists (`UpdateCheckService`, cached, never blocks a render). Triggering an update is **off by default** and needs three independent conditions: `MeterVault__AllowInAppUpdate`, at least one configured API key, and a caller presenting one (constant-time compare). `AllowAnonymousApi` deliberately cannot reach it — the updater builds whatever is on the branch and the LXC runs the app as root, so this is root-equivalent. Launches detached via `systemd-run` because the update restarts the service. Treat any change here as security-critical; `UpdateRunnerTests` pins the gate.
|
||||
**In-app update (`UpdateRunner`):** the dashboard shows a banner when a newer tag exists (`UpdateCheckService`, cached, never blocks a render). Triggering an update is **off by default**; `MeterVault__AllowInAppUpdate` is the *only* gate — no API key, by explicit owner decision. With it on, anything that can reach the app can trigger a rebuild+restart as root (realistically a DoS, since the build comes from the owner's own repo; RCE if that repo is compromised). The REST endpoint additionally requires an `X-MeterVault-Update` header — a CSRF guard, not auth, so a foreign page cannot drive it via a LAN browser. Launches detached via `systemd-run` because the update restarts the service. Treat any change here as security-critical; `UpdateRunnerTests` pins that the flag defaults off and that API keys alone don't enable it.
|
||||
|
||||
**Secrets (SDD §6.4):** broker/HA tokens are **never** stored in DB plaintext. Two forms, chosen per connector in the admin UI: a *reference* (`token_env`/`password_env` naming an env var or Docker secret path) resolved at runtime, or *encrypted at rest* (`token_enc`/`password_enc`) via `SecretProtector` over the ASP.NET Core data-protection key ring. Exactly one survives a save; `EndpointSecret.Resolve` is the single resolution path (encrypted wins). The key ring lives outside the app directory (`MeterVault__DataProtectionKeyPath`, default `/var/lib/metervault/keys`) because the LXC updater republishes `/opt/metervault`. `ExportService` drops `*_enc` values — they are bound to the originating key ring.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user