This commit is contained in:
Mateusz Gruszczyński committed 2026-10-10 20:56:36 +02:00
1 parent f213d853e2
commit 76b8bb7ee2
78 files changed
+7750 -5204

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
# RustProxy 0.31.0 - API, panel and HTTP cache-maintenance security review
Scope: source snapshot supplied for the v0.31.0 maintenance follow-up, reviewed
2026-10-10. This is a **code/contract review with offline regressions**, not a
penetration test or proof of production safety. The supplied archive has no
`.git` directory; the review does not establish parity with remote `master`.
## Interfaces and trust boundaries
| Surface | Exposure | Authentication and mutation rules | Assessment |
|---|---|---|---|
| Public administration bootstrap (`/`, login JS/CSS, `/api/auth/status`, setup/login POST) | Login/public administration listener | Host/origin validation; request marker for unsafe bootstrap actions; one-time setup capability and login throttling | Source-reviewed; live browser flow pending |
| Private panel HTML, JS, documentation, OpenAPI | Administration listener | Administrator session; `Cache-Control: no-store`; CSP, frame and MIME protections | Source and anonymous-response test reviewed; runtime pending |
| `/api/status`, `/api/system-info`, `/api/traffic`, `/api/cache`, logs, config, preferences, audit | Administration listener | Administrator cookie session; sensitive reads not on anonymous allowlist | Source and regression tests added; runtime pending |
| `/api/events`, live-log SSE | Administration listener | Session on subscribe and repeated validity checks; bounded viewers; heartbeat/disconnect cleanup | Source-reviewed; reconnect/load pending |
| State-changing admin APIs (including cache/config/preferences) | Administration listener | Valid session, Origin/Host, `X-RustProxy-Request: 1`, CSRF token; audit on cache commands | Source and regression tests added; runtime pending |
| Certificate API (`/api/certificates/{id}/reload`, deploy) | Administration listener | Narrow-scoped Key ID + Bearer grant; no general read access; HTTPS required remotely; origin validation when supplied | Source-reviewed; token rotation/revocation live tests pending |
| Public `BAN`/`PURGE` | Configured public vhost listener | **No admin session**: separate explicit IP/CIDR allowlist (empty by default), matched listener/vhost/ACL and optional rate limit | Hardened below; reverse/HTTP tests pending |
| Separate Prometheus metrics exporter | Distinct optional listener | Configured IP CIDRs and bearer-token policy; not the admin session | Unchanged; deployment exposure audit pending |
## Findings addressed in this follow-up
1. **E0716 / compilation defect (high functional impact).** `error()` accepts
`&'static str`; a reference to `format!(...)` was a temporary. Invalid
selector errors now log the detailed reason server-side and return a fixed,
non-sensitive message with status 422. This removes the reported lifetime
violation; the Rust compiler must still verify the entire crate.
2. **Cache protocol-scope confusion (medium).** Path-only `BAN`/`PURGE`
selectors could affect representations of the same host/path across both
HTTP and HTTPS. Targets are now built with the trusted external scheme,
validated to `http`/`https`, and the matched Host/vhost. IPv6 literal Hosts
are bracketed for the URL parser. Other tenants, schemes and hosts remain
outside the selector.
3. **Unbounded blocking maintenance work (medium availability risk).** Cache
invalidation may scan hot/cold entries. Both public HTTP and authenticated
admin cache purge/selective invalidation share two maintenance slots and
execute scans with `spawn_blocking`. Bursts receive 429 instead of piling
up unbounded blocking work on Tokio core threads. The permit stays with the
worker after a client disconnects. This is a concurrency bound, **not** a
per-client rate quota or a hard time limit for disk work.
4. **Ambiguous maintenance input (medium).** Duplicate TTL/confirmation or
`Content-Length` headers, transfer-encoded bodies, nonempty bodies and
upgrade requests are rejected; `Content-Length: 0` remains compatible with
CLI clients. TTL is limited to 1..604800 s. Broad `BAN /` requires the
explicit confirmation header; `PURGE` cannot use BAN-only controls.
5. **Audit ordering (low/medium).** Removal of an administrator-created BAN
rule now records the audit entry before mutating state. Unavailable audit
storage blocks the mutation rather than allowing an unaudited removal.
6. **Operator safety/usability (low).** An EN/PL help popover beside the HTTP
allowlist gives exact `curl` syntax, IP-only authentication semantics and
reverse-proxy identity requirements. No new global config variable is added.
## Existing protections checked in source
- Admin router's exact anonymous method/path allowlist contains only the
expected authentication bootstrap APIs; private docs and assets remain
session-protected. Request guard rejects duplicate Host/Origin and checks
browser `Sec-Fetch-Site` cross-site signals.
- Mutation guard uses an Origin/Host match, per-session CSRF comparison and
`X-RustProxy-Request` marker; cookie sessions are HttpOnly/SameSite=Strict
and use Secure under HTTPS/trusted forwarded protocol.
- Secure response policy includes CSP, `X-Frame-Options: DENY`,
`X-Content-Type-Options: nosniff`, referrer restrictions and no-store for
private material. A static scan checked 27 first-party browser JS files for
direct unsafe HTML injection and dynamic `eval`/`Function` sinks; this scan
**cannot establish that the entire DOM is XSS-free**.
- IP trust comes from the transport peer or a validated forwarding chain only
when the peer is configured trusted. An untrusted client-supplied XFF does
not independently grant BAN/PURGE permission. The cache allowlist rejects
`/0`, invalid CIDRs, duplicates and more than 32 entries. Requests are never
forwarded upstream.
- Certificate API tokens have explicit operation/ID grants and cannot use the
general admin read API; Bearer grant operations are intentionally separate
from cookie + CSRF authentication.
## HTTP maintenance CLI (allowlisted client only)
```sh
# One URL, including the query string: 200 with JSON result.
curl -i -X PURGE 'https://site.example/assets/app.js?v=3'
# Lazily ban previously cached objects under this path: 202 with JSON result.
curl -i -X BAN 'https://site.example/assets/'
# Entire host: explicit opt-in confirmation header.
curl -i -X BAN 'https://site.example/' -H 'X-RustProxy-Confirm-Wide: 1'
# Optional BAN expiration (30 seconds).
curl -i -X BAN 'https://site.example/assets/' -H 'X-RustProxy-Ban-Ttl: 30'
```
Use **Settings -> Cache -> HTTP BAN / PURGE IP allowlist** to grant the
caller IP/CIDR. This HTTP interface does not accept admin cookies/tokens as an
alternative to the allowlist. The domain, Host header, external scheme and
listener must select the intended vhost. Behind a reverse proxy, forward the
non-standard methods untouched and **replace**, rather than append untrusted,
XFF/Forwarded headers at the edge. Never add the entire reverse-proxy subnet
as a maintenance client merely because it is trusted for forwarding.
## Release security test plan (requires compiled RustProxy)
1. On the deployment host, run `cargo fmt --all -- --check`,
`./dev.sh check`, `./dev.sh test`, and `./dev.sh release`. Address any
compiler/regression errors before installation. `cargo audit` or an
equivalent advisory scan requires an up-to-date advisory DB and was not run
in this offline environment.
2. Start a disposable instance with a populated cache under both HTTP and
HTTPS for the same vhost/URL. Confirm `PURGE` returns 200 and drops the
exact query variant only; `BAN` returns 202 and invalidates only **older**
entries on the URL prefix. Confirm the *other* scheme and vhost stay hot.
3. From a denied IP, expect 403 on `BAN` and `PURGE`. With an empty allowlist,
all clients must be denied. Send duplicate TTL/confirmation/Content-Length,
TE/body and `BAN /` without confirmation: expect 400. On a trusted-proxy
hop with missing real client identity, expect 403. Test both IPv4 and IPv6.
4. From an allowed IP, send 10 concurrent broad maintenance requests; confirm
that only two workers run simultaneously and excess return 429. Verify
server CPU/IO and audit entries while the cold tier is populated.
5. Send GET requests without admin cookies to `/api/config`, `/api/status`,
`/api/system-info`, `/api/events`, `/api/logs`, `/docs/openapi.json` and
`/assets/app.js`; expect 401 and protective headers. With valid cookie but
missing/bad Origin, marker or CSRF on PUT/POST/DELETE, expect 403. Test
logout and password/session invalidation, live SSE reconnect/expiry.
6. Test the Certificate API with no grant, a valid certificate-specific grant,
revoked token, HTTP remote peer, and misuse against `/api/config`/logs.
Check errors for token disclosure and audit coverage.
7. Test actual browser startup, CSP console, output escaping and form save
with malicious HTML strings; inspect reverse-proxy Host/Origin forwarding.
Prefer HTTPS administration with a strict `allowed_hosts` list.
## Residual risk / non-claims
- **Not executed in this environment:** Cargo format/check/tests/release,
actual Rust listener requests, HTTP/2/3, fuzzing, penetration tests,
session revocation under load, proxy deployment configuration, dependency
CVE scanning and long-running cold-cache resource measurements.
- Proxy trust configuration is security-critical. A misconfigured edge that
accepts forged forwarded client addresses can let external users impersonate
an allowlisted IP. Restrict direct listener reachability and sanitize headers.
- A public IP allowlist is not user authentication; anyone using an allowed
IP/network can issue destructive maintenance commands. Use narrow CIDRs and
optionally enforce additional controls on the edge (mTLS, firewall).
- The two-worker concurrency bound mitigates bursts, not all denial-of-service
scenarios: configured cache size and storage latency still determine scan
cost. Per-IP quotas are not provided here.
- Proxy protocol path, certificate verification, arbitrary upstream code,
all application content and third-party packages are **not certified safe**
by this audit. Production acceptance remains open until the runtime test
plan is completed.