Skip to content

both /metrics endpoints are public and unauthenticated in prod; cryptify's guard shipped but was never configured #372

Description

@rubenhensen

Question

Both /metrics endpoints are publicly reachable and unauthenticated in production right now. Found while scoping postguard-ops#64.

cryptify — a guard exists and was never switched on

https://storage.postguard.eu/metrics serves the full exposition to anyone. The container binds 127.0.0.1:8002:8000, which alone would be host-local, but the nginx vhost proxies location / with no path exclusions (procolix/main.tf:97-103), so it is exposed.

The fix needs no code: cryptify/src/main.rs:140-159 already requires Authorization: Bearer <token>, constant-time compared, when metrics_token is configured — shipped in cryptify#177 and present in prod's 0.1.29. It is simply never set: absent from the rendered config.toml heredoc (procolix/main.tf:20-37), absent from procolix/variables.tf, and no ROCKET_METRICS_TOKEN in the env file. So prod logs the warning the code emits for exactly this case:

metrics_token is not set — /metrics is publicly accessible without authentication.

The k8s cryptify (dev) has the same gap (main.tf:418-445).

pg-pkg — no guard exists at all

https://pkg.postguard.eu/metrics is public. /metrics is registered on the root App outside the /v2 scope (pg-pkg/src/server.rs:368-370), so the Governor rate limiter and Auth middleware — both wrapped on /v2 only (server.rs:265, 341) — never see it. The ingress rule is path: "/", Prefix (main.tf:1231-1250), and the NetworkPolicy that would restrict it is count = !var.deploy_pkg_ingress ? 1 : 0 (main.tf:1370), i.e. not created in either environment, since both set deploy_pkg_ingress = true.

So closing pg-pkg's needs either an ingress path rule / server-snippet denying /metrics, a second listener, or a code change. Unlike cryptify, there is no knob to set.

Why it is this shape

cryptify#102 opened as a draft on this exact question"/metrics is unauthenticated; restrict at the firewall / reverse proxy. Confirm this fits the Scaleway / Procolix network policy before merging." The confirmation never arrived and it merged anyway. This is the residue.

Sequencing — deliberately after the scraper, not before

Locking down first is the tempting order and the wrong one. Under #64's decision the collector is push-based: Alloy scrapes 127.0.0.1:8002 on the Procolix host itself, so closing the public path does not break it. Do it in the other order and you create the network dependency the push design exists to avoid.

Scope

  • Add a sensitive cryptify_metrics_token variable, thread it into the config, set it in both deployments.
  • Decide and implement pg-pkg's mechanism (ingress rule vs code change).
  • Confirm from outside the network that both endpoints 401 afterwards, and that the collector still reports.

Note on severity

The exposure is metrics, not message content or key material — upload counts, storage totals, client versions, process stats. Real, but calibrate it against GHSA-5rhx-xgvv-h78h, which was per-tenant usage for an arbitrary address. What makes it worth its own ticket is that one half is a config value that was shipped and never set.

Part of #247 (workstream D).

Metadata

Metadata

Assignees

No one assigned

    Labels

    repo:postguardTouches the postguard repowayfinder:taskWayfinder ticket: manual work unblocking a decision, or execution under this map

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions