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:
@@ -74,7 +74,7 @@ Configuration is via environment variables (`Section__Key` double-underscore map
|
||||
| `MeterVault__SeedReferenceData` | `true` to load the bundled demo dataset on first start (idempotent) |
|
||||
| `MeterVault__DataProtectionKeyPath` | Where the key ring for UI-entered connector secrets lives (default `/var/lib/metervault/keys`) |
|
||||
| `MeterVault__UpdateCheckEnabled` | `false` to stop the dashboard checking for a newer release |
|
||||
| `MeterVault__AllowInAppUpdate` | `true` to allow updates triggered from the UI/API — grants root-equivalent access to anyone with an API key; see below |
|
||||
| `MeterVault__AllowInAppUpdate` | `true` to allow updates triggered from the UI/API — no key required, so anything that can reach MeterVault can trigger one; see below |
|
||||
| `MeterVault__UpdateCheckUrl` | Tag listing consulted by that check (repoint at a fork; blank also disables it) |
|
||||
|
||||
The REST API is **closed by default**: with no `ApiKeys` configured and `AllowAnonymousApi` off, it
|
||||
@@ -96,20 +96,25 @@ sent — cached for six hours, and it never blocks or fails a page render. Turn
|
||||
`POST /api/v1/system/update` endpoint for scripting it from Home Assistant or `curl`:
|
||||
|
||||
```bash
|
||||
curl -X POST http://metervault:8760/api/v1/system/update -H "X-Api-Key: $METERVAULT_API_KEY"
|
||||
curl -X POST http://metervault:8760/api/v1/system/update -H "X-MeterVault-Update: 1"
|
||||
```
|
||||
|
||||
Both pull the latest source, rebuild, and restart the service — a few minutes during which MeterVault
|
||||
is unavailable. Readings are untouched; ingestion resumes on restart. LXC only: containers are
|
||||
replaced by pulling a new image, and the endpoint reports that rather than pretending.
|
||||
|
||||
> **Understand what this grants before enabling it.** The updater builds whatever is on the branch,
|
||||
> and the LXC runs MeterVault as **root** — so a valid API key becomes arbitrary code execution on
|
||||
> that host. Three things must all hold before anything runs: the opt-in above, at least one
|
||||
> configured API key, and a caller presenting one. In particular `MeterVault__AllowAnonymousApi` can
|
||||
> **never** reach it — opening reads must not open root — and the UI button asks for the key every
|
||||
> time rather than remembering it, because the UI itself has no login. Leave this off unless the UI
|
||||
> is behind an authenticating proxy or on a network you fully trust.
|
||||
> **That flag is the whole gate — there is no key and no prompt.** With it on, anything that can
|
||||
> reach MeterVault can trigger a rebuild and restart. Realistically that is a repeatable denial of
|
||||
> service (minutes of downtime and a busy CPU per request), not code injection, because the build
|
||||
> comes from your own repository — but it becomes remote code execution if that repository is ever
|
||||
> compromised. It defaults off. Enable it only on a network you trust, or behind an authenticating
|
||||
> proxy.
|
||||
>
|
||||
> The `X-MeterVault-Update` header is **not** authentication: it stops a *different website* driving
|
||||
> the endpoint through the browser of someone on your network, which a plain HTML form could
|
||||
> otherwise do. The UI button does not need it — it runs over the Blazor circuit, which a foreign
|
||||
> page cannot reach. Every triggered update is logged as a warning, since with no key there is no
|
||||
> caller to attribute it to.
|
||||
|
||||
Secrets (broker/HA tokens) are **never** stored in the database as plaintext. Each connector picks
|
||||
one of two forms: the *name* of an environment variable, resolved at runtime, or the secret typed
|
||||
|
||||
Reference in New Issue
Block a user