Files
RustProxy/docs/V0.22.3_AUDIT.md
T
2026-10-03 00:27:09 +01:00

6.5 KiB

RustProxy 0.22.3 specification audit

Scope

This audit covers the 0.22.3 delta over 0.22.2: downstream HTTP/1.1 Upgrade: h2c, dedicated certificate hot reload, and deployment of certificate/key pairs owned by an external local issuer such as system Certbot. Upstream mTLS/client-certificate support is intentionally deferred.

Requirement matrix

Requirement Source implementation Regression / verification
Preserve h2c prior knowledge cleartext listeners still sniff the HTTP/2 connection preface before entering HTTP/1 existing prior-knowledge regression plus source/release checks
Accept HTTP/1.1 Upgrade: h2c only when enabled H1 service checks the listener h2c capability before returning 101 malformed_h2c_upgrade_is_rejected_before_switching_protocols and source checks
Validate the Upgrade request requires HTTP/1.1, Connection: Upgrade, HTTP2-Settings, exactly one Upgrade: h2c, one bounded base64url settings payload, Host/authority, no request body h2c_upgrade_validation_rejects_missing_settings_and_bodies
Reconstruct H1 request as H2 stream 1 bounded HPACK bootstrap encodes pseudo/header fields and injects a stream-1 HEADERS frame after client preface + initial SETTINGS http1_h2c_upgrade_bootstraps_request_as_http2_stream_one source regression
Keep later streams on normal H2 engine upgraded I/O is handed to the same Hyper HTTP/2 server builder used by native H2 source contract review; runtime gate open here
Bound malformed bootstrap traffic initial preface/SETTINGS/header block have explicit size/shape checks; invalid input closes/rejects rather than entering routing source checks + malformed request test source
Dedicated certificate reload API authenticated POST /api/certificates/reload invokes App::reload_certificates admin regression + OpenAPI/source checks
Reload does not mutate desired config/revision recompile uses the existing revision and only swaps the immutable effective snapshot certificate_reload_keeps_configuration_revision_stable
Failed reload preserves active snapshot compilation completes before ArcSwap publication; an error returns without store source review; release-host negative runtime acceptance required
Public H1/H2/H3 new handshakes use refreshed cert public TCP TLS reads the current snapshot per ClientHello; QUIC config is rebuilt in the published snapshot source review; live release-host acceptance required
Existing sessions are not interrupted reload swaps configuration state only; it does not cancel public connection/tunnel tokens source review
External Certbot/manual pair can be deployed tls deploy --id ... --cert ... --key ... reads local PEMs and calls authenticated POST /api/certificates/deploy; the service stages a generation and publishes the existing manual certificate id CLI/API/source checks + external pair staging regression
Certbot live/*.pem symlinks work local CLI canonicalizes the supplied path once, then opens the resolved regular file with bounded reads source checks; live Certbot-path acceptance recommended
Private key safety external key is size-bounded, must be a regular file and on Unix may not grant group/world permissions; staged files use private creation semantics source review + existing TLS file safety contracts
Pair/SAN validation before publication stage_external_pair calls the existing certificate/key parser and extracts DNS SANs external_pair_stages_generation_and_rolls_back_cleanly
Atomic multi-vhost switch existing certificate id keeps all vhost assignments; only its generation filenames/coverage change in one revision-CAS config update source/API review
No dual owner with built-in ACME tls deploy rejects entries carrying RustProxy issuer metadata source contract checks
Roll back staged files if publication fails failed config/API publication invokes remove_external_pair for the staged generation source review + helper regression
Administration TLS caveat is explicit reload/deploy report that startup-fixed administration TLS needs restart when it selects the affected inventory certificate API/CLI/UI/OpenAPI checks
Upstream mTLS intentionally not part of 0.22.3 deferred by product decision

h2c bootstrap semantics

The Upgrade path is enabled only on a cleartext listener that already opts into h2c. It does not make ordinary HTTP listeners opportunistically negotiate HTTP/2. A valid H1 request receives 101 Switching Protocols; the client then sends the normal HTTP/2 connection preface and its initial non-ACK SETTINGS frame. RustProxy inserts a synthetic body-free HEADERS frame for stream 1 and lets Hyper own subsequent H2 framing, flow control, stream lifecycle and graceful shutdown.

The bootstrap deliberately rejects H1 request bodies. This avoids replaying or buffering a body across the protocol switch and keeps the transition bounded. Prior-knowledge h2c remains unchanged.

External certificate ownership model

There are two supported external workflows:

  1. Generation deploy (recommended for Certbot): a local deploy hook calls tls deploy. The CLI reads Certbot's current live/ targets and sends the bounded PEM pair to authenticated loopback POST /api/certificates/deploy; the running service validates and creates a new immutable pair in its private certificate directory, then switches an existing manual certificate id through the normal revisioned configuration API. Every vhost already assigned to that id moves together.
  2. In-place file replacement: an operator replaces files already referenced by the desired configuration through another safe local mechanism, then calls POST /api/certificates/reload or tls reload. The desired configuration and revision do not change.

RustProxy-managed ACME ids are excluded from external deploy so the same certificate lineage cannot have two renewal owners. The API does not accept arbitrary host filesystem paths from a remote request.

Release gate

This packaging workspace has no cargo, rustc or rustfmt. Rust parsing/type checking, compilation, linking and Rust test execution cannot be claimed here. Rust-independent JavaScript/JSON/language/source/package/Go gates are run locally. A Rust-enabled release host must still run bash dev.sh doctor, bash dev.sh check, bash dev.sh test, a release build, the raw H1 -> h2c Upgrade -> H2 stream-1 test, native h2c prior-knowledge, TLS/H2/H3 certificate hot reload, invalid-certificate rollback and normal H1/H2/H3 acceptance before deployment.