feat(security): opt-in authentication for /metrics (SEC-14)
New MxGateway:Metrics:RequireAuthentication (default false). When true, /metrics requires an API key with the admin scope (verified through the cached verifier and the shared API-key failure limiter) or a dashboard login with a real dashboard role; anonymous localhost and Authentication:Mode=Disabled do not qualify. An anonymous scrape gets a plain 401 Bearer challenge, a throttled peer 429. /health stays anonymous because the off-repo Overview dashboard polls /health/active. MapZbMetrics returns IEndpointConventionBuilder, so the policy is attached with RequireAuthorization.
This commit is contained in:
@@ -419,6 +419,35 @@ model requires otherwise.
|
||||
| `MxGateway:Security:AuditRetentionDays` | `90` | Days of canonical audit history kept in the `audit_event` table. The audit drain sweeps once at startup and hourly thereafter, deleting rows older than this window; without it the table grows without bound inside the same SQLite file the authentication hot path reads. Rows whose timestamp SQLite cannot parse are never swept. Must be greater than zero — retention can be widened but not switched off. |
|
||||
| `MxGateway:Security:ApiKeyFailureTrackedPeers` | `4096` | Maximum distinct partitions tracked by the failure counter (a bounded LRU) so a spray of unique tokens cannot grow memory without limit. It cannot be used to flush an active block either: only a validly shaped `mxgw_<keyId>_<secret>` token mints a key-id partition (everything else lands on the sender's transport-peer partition), each address may mint at most 32 key-id partitions before the overflow collapses onto that address's fallback partition, and eviction prefers fully expired windows, never removing an over-limit partition until the map exceeds twice this cap. Must be greater than zero. |
|
||||
|
||||
## Metrics Options
|
||||
|
||||
Bound from `MxGateway:Metrics`. Governs the Prometheus `/metrics` scrape endpoint
|
||||
(mounted by the shared `ZB.MOM.WW.Telemetry` package). The health endpoints
|
||||
(`/healthz`, `/health/ready`, `/health/active`) are not affected by this section and
|
||||
always stay anonymous: the family Overview dashboard polls `/health/active` from
|
||||
another repo without credentials, so gating it would break that consumer.
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `MxGateway:Metrics:RequireAuthentication` | `false` | When `true`, `/metrics` requires an authenticated caller (SEC-14). A scrape is accepted when it presents an API key carrying the `admin` scope as `Authorization: Bearer mxgw_<keyId>_<secret>`, or — with no authorization header — a dashboard login cookie holding a real dashboard role (`Administrator` or `Viewer`). Anonymous localhost access and `Authentication:Mode=Disabled` do **not** satisfy it. An anonymous scrape gets `401` with `WWW-Authenticate: Bearer`; an authenticated caller without `admin` scope or a dashboard role gets `403`; a peer throttled by the API-key failure limiter (`MxGateway:Security:ApiKeyFailure*`, shared with the gRPC path) gets `429`. The default keeps the endpoint anonymous so existing scrapers keep working. |
|
||||
|
||||
Configure a Prometheus scraper against an authenticated endpoint with a dedicated
|
||||
admin-scoped key, for example:
|
||||
|
||||
```yaml
|
||||
scrape_configs:
|
||||
- job_name: mxgateway
|
||||
authorization:
|
||||
type: Bearer
|
||||
credentials_file: /etc/prometheus/mxgateway.key # contains mxgw_<keyId>_<secret>
|
||||
static_configs:
|
||||
- targets: ["gateway-host:5130"]
|
||||
```
|
||||
|
||||
The setting is read from configuration when endpoints are mapped, so changing it
|
||||
requires a restart. With dev-only `Dashboard:DisableLogin=true` every request is
|
||||
auto-authenticated as an Administrator, which also satisfies `/metrics`.
|
||||
|
||||
## Galaxy Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|
||||
Reference in New Issue
Block a user