149 lines
10 KiB
Markdown
149 lines
10 KiB
Markdown
# 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.
|