Skip to content

api: restore access control on the unified Prometheus /metrics endpoint - #63

Merged
priscaenoch merged 1 commit into
octraban:mainfrom
samuel2926i39-art:fix/unified-metrics-endpoint-issue-52
Aug 24, 2026
Merged

api: restore access control on the unified Prometheus /metrics endpoint#63
priscaenoch merged 1 commit into
octraban:mainfrom
samuel2926i39-art:fix/unified-metrics-endpoint-issue-52

Conversation

@samuel2926i39-art

Copy link
Copy Markdown
Contributor

Summary

The API service already had a single, documented-in-code GET /metrics exposing the shared prom-client registry (src/metrics.ts via src/index.ts) — most of #52 was already in place. What was missing:

  1. Access control regressed silently. Commit 81e6186 (fix(security): protect GET /metrics with operator token or loopback restriction) added METRICS_TOKEN/loopback gating on this route. A later, unrelated CI-fix commit (6edbf655, fix(ci): resolve compilation issues and restore missing proxyTrust middleware) was based on a pre-81e6186 copy of src/index.ts and its merge silently dropped that gating — main has been serving /metrics with no access control since. tests/metrics-access.test.ts didn't catch this because it re-implemented the guard logic inline in a throwaway Express app instead of exercising the real route, so it kept passing straight through the regression.
  2. No README documentation of the endpoint, the two services' separate registries, or METRICS_TOKEN.

What changed

  • Extracted the guard into src/middleware/metricsAuthGuard.ts (same pattern as the existing src/middleware/metricsMiddleware.ts) and wired it onto GET /metrics in src/index.ts. Behavior (unchanged from the original 81e6186 design): METRICS_TOKEN set → Authorization: Bearer <token> or X-Metrics-Token required, 401 otherwise; METRICS_TOKEN unset → only loopback (127.0.0.1/::1) allowed, 403 for remote callers.
  • Rewrote tests/metrics-access.test.ts to import and exercise the real metricsAuthGuard (via supertest against a minimal app that mounts it) instead of a hand-rolled copy, so a future regression here fails the suite instead of silently passing.
  • Added a ## Metrics section to README.md documenting both services' /metrics endpoints (API :3000 vs indexer :3001, their separate registries, default process metrics) and the METRICS_TOKEN env var, plus a /metrics row in the API Endpoints table.

Context / before-after

  • Before: curl http://<host>:3000/metrics returned the full metrics payload to anyone, unauthenticated — an internal-topology/ops-visibility leak on a publicly reachable service. No mention of the endpoint or METRICS_TOKEN in the README.
  • After: same endpoint, same registry, but gated exactly as originally designed and shipped in 81e6186; documented for operators.
  • No change to what's exposed once authorized — same registry, same metric set.

Testing

  • Added/updated tests/metrics-access.test.ts — now imports the real metricsAuthGuard from src/middleware/:
    • 401 with no credentials / wrong Bearer token / wrong X-Metrics-Token when METRICS_TOKEN is set; 200 with the correct token via either header.
    • 403 for non-loopback callers, 200 for loopback, when METRICS_TOKEN is unset.
    • JSON error bodies on both 401 and 403.
  • Ran locally: vitest run tests/metrics-access.test.ts tests/metrics-middleware.test.ts → 17/17 passing.
  • tsc --noEmit — clean.
  • eslint src/middleware/metricsAuthGuard.ts src/index.ts tests/metrics-access.test.ts — clean.

Closes #52

…point

GET /metrics (src/index.ts) already exposed the shared prom-client
registry, but the operator-token/loopback access control added in
81e6186 was silently dropped by an unrelated CI-fix merge (6edbf65)
and the endpoint has been unauthenticated on main since. The existing
tests/metrics-access.test.ts masked this: it re-implemented the guard
logic in a throwaway Express app instead of exercising the real route,
so it kept passing after the regression.

- Extract the guard into src/middleware/metricsAuthGuard.ts (mirrors
  the existing metricsMiddleware.ts convention) and wire it onto
  GET /metrics. METRICS_TOKEN set -> Bearer/X-Metrics-Token required
  (401 otherwise); unset -> loopback-only (403 for remote callers).
- Rewrite tests/metrics-access.test.ts to import and exercise the real
  metricsAuthGuard via supertest, so a future regression here fails
  the suite instead of silently passing.
- Document the endpoint, both services' registries, and METRICS_TOKEN
  in README.md.

Closes octraban#52
@priscaenoch
priscaenoch merged commit b064c9b into octraban:main Aug 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose and document a unified Prometheus metrics endpoint

2 participants