Add tower-governor rate limits and trim public indexer status metrics (#328) #502

Merged
PlasticDigits merged 1 commit from issue-328-rate-limit-status-trim into main 2026-06-15 01:02:46 +00:00
PlasticDigits commented 2026-06-14 23:04:16 +00:00 (Migrated from gitlab.com)

Summary

Implements GitLab #328 (parent #325 F-07/F-08): in-process per-peer HTTP rate limiting via tower_governor, and a trimmed public GET /v1/status surface.

  • tower_governor on all routes except GET /healthz (defaults: INDEXER_RATE_LIMIT_PER_MIN=600, INDEXER_RATE_LIMIT_BURST=120; 0 disables). Returns 429 with {"error":"rate limit exceeded"}.
  • INDEXER_TRUST_PROXY=1 uses SmartIpKeyExtractor for reverse-proxy deployments.
  • Public GET /v1/status (schema 2.13.0) exposes only schema_version, database_connected, max_indexed_block, ingestion_alive.
  • GET /v1/status/ops serves full rpc_metrics / chain_pointer when INDEXER_EXPOSE_OPS_METRICS=1.
  • Docs, invariants (INV-INDEXER-328-*), verify scripts, and agent skill cross-links updated.

Acceptance checklist

Criterion Command / step Result
In-process rate limiting; abuse returns 429 bash scripts/verify-indexer-rate-limit.sh PASS
Arena UI / indexer-first display under normal traffic bash scripts/verify-podium-live-anvil.sh PASS
Public /v1/status omits rpc_metrics by default bash scripts/verify-indexer-rpc-metrics.sh (checks public + ops) PASS
/healthz suitable for LB probes bash scripts/verify-indexer-rate-limit.sh (healthz=200 under throttle) PASS
cargo clippy + cargo test cd indexer && cargo clippy --all-targets -- -D warnings && cargo test PASS
Integration tests cd indexer && cargo test --test integration_stage2 (needs YIELDOMEGA_PG_TEST_URL) PASS
Frontend unaffected cd frontend && npm run typecheck && npm test PASS

Verification for third parties

  1. bash scripts/bootstrap-cloud-postgres-native.sh (or existing Postgres on :5433).
  2. export PATH="$HOME/.foundry/bin:$PATH" and source /usr/local/cargo/env.
  3. bash scripts/verify-indexer-rate-limit.sh
  4. bash scripts/verify-indexer-rpc-metrics.sh
  5. bash scripts/verify-podium-live-anvil.sh
  6. cd indexer && cargo clippy --all-targets -- -D warnings && cargo test

Env knobs documented in indexer/.env.example and indexer/README.md §328.

  • Parent: #325 recommendation #4 (in-process rate limits, no Cloudflare/nginx requirement)
  • Issue: #328
## Summary Implements GitLab #328 (parent #325 F-07/F-08): in-process per-peer HTTP rate limiting via `tower_governor`, and a trimmed public `GET /v1/status` surface. - **`tower_governor`** on all routes except `GET /healthz` (defaults: `INDEXER_RATE_LIMIT_PER_MIN=600`, `INDEXER_RATE_LIMIT_BURST=120`; `0` disables). Returns **429** with `{"error":"rate limit exceeded"}`. - **`INDEXER_TRUST_PROXY=1`** uses `SmartIpKeyExtractor` for reverse-proxy deployments. - Public **`GET /v1/status`** (schema **2.13.0**) exposes only `schema_version`, `database_connected`, `max_indexed_block`, `ingestion_alive`. - **`GET /v1/status/ops`** serves full `rpc_metrics` / `chain_pointer` when **`INDEXER_EXPOSE_OPS_METRICS=1`**. - Docs, invariants (`INV-INDEXER-328-*`), verify scripts, and agent skill cross-links updated. ## Acceptance checklist | Criterion | Command / step | Result | |-----------|----------------|--------| | In-process rate limiting; abuse returns 429 | `bash scripts/verify-indexer-rate-limit.sh` | PASS | | Arena UI / indexer-first display under normal traffic | `bash scripts/verify-podium-live-anvil.sh` | PASS | | Public `/v1/status` omits `rpc_metrics` by default | `bash scripts/verify-indexer-rpc-metrics.sh` (checks public + ops) | PASS | | `/healthz` suitable for LB probes | `bash scripts/verify-indexer-rate-limit.sh` (healthz=200 under throttle) | PASS | | `cargo clippy` + `cargo test` | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | PASS | | Integration tests | `cd indexer && cargo test --test integration_stage2` (needs `YIELDOMEGA_PG_TEST_URL`) | PASS | | Frontend unaffected | `cd frontend && npm run typecheck && npm test` | PASS | ## Verification for third parties 1. `bash scripts/bootstrap-cloud-postgres-native.sh` (or existing Postgres on `:5433`). 2. `export PATH="$HOME/.foundry/bin:$PATH"` and `source /usr/local/cargo/env`. 3. `bash scripts/verify-indexer-rate-limit.sh` 4. `bash scripts/verify-indexer-rpc-metrics.sh` 5. `bash scripts/verify-podium-live-anvil.sh` 6. `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` Env knobs documented in `indexer/.env.example` and `indexer/README.md` §328. ## Related - Parent: #325 recommendation #4 (in-process rate limits, no Cloudflare/nginx requirement) - Issue: #328
PlasticDigits commented 2026-06-14 23:08:05 +00:00 (Migrated from gitlab.com)

Security review — MR !502

Commit reviewed: 4b1daab6f697ffd2dc66e548229ba3c3be5cbb49
Scope: indexer/src/rate_limit.rs, indexer/src/api.rs, indexer/src/main.rs, indexer/Cargo.toml, verify scripts, docs/skill cross-links (#328).

Outcome: FINDINGS: 0 medium+ — NONE

Summary

Reviewed added/modified code for injection, authn/authz bypass, info disclosure, SSRF/XSS, and rate-limit bypass paths. No prior security-review threads on this MR.

Rate limiting (tower_governor):

  • Default path uses PeerIpKeyExtractor + into_make_service_with_connect_info — client IP from TCP ConnectInfo, not attacker-controlled X-Forwarded-For.
  • INDEXER_TRUST_PROXY=1 enables SmartIpKeyExtractor (documented for reverse-proxy deployments only). Spoofed XFF bypass is a known misconfiguration class when the indexer is reachable without a trusted proxy stripping client headers; default is off.
  • /healthz exemption is intentional for LB probes; handler is minimal (200 ok).
  • retain_recent() sweep limits governor storage growth.

Status surface trim:

  • Public GET /v1/status now omits rpc_metrics, chain_pointer, and last_indexed_at_ms (reduced disclosure vs pre-MR).
  • GET /v1/status/ops returns detailed metrics only when INDEXER_EXPOSE_OPS_METRICS=1 (default off); otherwise 404. No new auth boundary — this narrows what was previously always public on /v1/status.

Other: No new SQL/shell sinks, no smart-contract changes, no secret logging. tower_governor 0.8.0 is a standard axum middleware dependency.

Inline threads: none (no medium+ findings).

## Security review — MR !502 **Commit reviewed:** `4b1daab6f697ffd2dc66e548229ba3c3be5cbb49` **Scope:** `indexer/src/rate_limit.rs`, `indexer/src/api.rs`, `indexer/src/main.rs`, `indexer/Cargo.toml`, verify scripts, docs/skill cross-links (#328). **Outcome:** `FINDINGS: 0` medium+ — **NONE** ### Summary Reviewed added/modified code for injection, authn/authz bypass, info disclosure, SSRF/XSS, and rate-limit bypass paths. No prior security-review threads on this MR. **Rate limiting (`tower_governor`):** - Default path uses `PeerIpKeyExtractor` + `into_make_service_with_connect_info` — client IP from TCP `ConnectInfo`, not attacker-controlled `X-Forwarded-For`. - `INDEXER_TRUST_PROXY=1` enables `SmartIpKeyExtractor` (documented for reverse-proxy deployments only). Spoofed XFF bypass is a known misconfiguration class when the indexer is reachable without a trusted proxy stripping client headers; default is **off**. - `/healthz` exemption is intentional for LB probes; handler is minimal (`200 ok`). - `retain_recent()` sweep limits governor storage growth. **Status surface trim:** - Public `GET /v1/status` now omits `rpc_metrics`, `chain_pointer`, and `last_indexed_at_ms` (reduced disclosure vs pre-MR). - `GET /v1/status/ops` returns detailed metrics only when `INDEXER_EXPOSE_OPS_METRICS=1` (default off); otherwise `404`. No new auth boundary — this **narrows** what was previously always public on `/v1/status`. **Other:** No new SQL/shell sinks, no smart-contract changes, no secret logging. `tower_governor 0.8.0` is a standard axum middleware dependency. **Inline threads:** none (no medium+ findings).
PlasticDigits commented 2026-06-15 01:02:46 +00:00 (Migrated from gitlab.com)

mentioned in commit 35cecc13fe

mentioned in commit 35cecc13fe770e4bcc4d167f3e7bfa95b00885e0
PlasticDigits (Migrated from gitlab.com) merged commit 35cecc13fe into main 2026-06-15 01:02:46 +00:00
PlasticDigits commented 2026-06-15 01:15:39 +00:00 (Migrated from gitlab.com)

mentioned in issue #328

mentioned in issue #328
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega!502
No description provided.