Skip to content

httpjet v0.0.1

Choose a tag to compare

@faratech faratech released this 17 Sep 00:28
· 9 commits to main since this release

httpjet v0.0.1 — September 2026: security hardening round + new subsystems

First tagged release, adopting semantic versioning.

This sync carries everything since the previous public snapshot (September 6): a set of new
opt-in subsystems, a transactional zero-downtime reload architecture, and a whole-codebase
security review whose twelve fixes are the centerpiece of this release. As always, every
subsystem below is opt-in — the default configuration serves exactly as before.

Security hardening round (whole-codebase review)

Five parallel review slices covered the request pipeline, transports, TLS and unsafe code, trust
and access control, backend gateways, cache and static serving, control surfaces, process
supervision, ACME, logging, and ops packaging. Twelve fixes shipped, all fail-closed:

Transport hardening

  • HTTP/2: frame and request-body deadlines on the production loop. The production (monoio)
    h2 loop never enforced frame_read_timeout/request_body_timeout — those deadlines existed
    only in a code path that doesn't run — so a peer trickling one byte per idle window could pin a
    connection, its header block, and its body buffers indefinitely. The production loop now
    mirrors the tokio-loop bookkeeping: partial frames must complete within the frame deadline
    (GOAWAY PROTOCOL_ERROR otherwise), and requests with decoded headers but no END_STREAM are
    reset with body accounting released after the body deadline.
  • HTTP/1.1: stalled sendfile is bounded. A zero-window reader used to cause ~10k
    sendfile(2) syscalls/s per connection on a pinned worker core, forever. Retries back off
    exponentially (100 µs → 12.8 ms, reset on progress) and a transfer with zero progress for 60 s
    closes the connection.
  • HTTP/1.1: duplicate identical Content-Length lines collapse to one on the forwarded
    header map, so a lenient upstream cannot re-parse two lines differently.
  • CPU pinning respects CPU_SETSIZE — available_parallelism() can exceed the fixed
    1024-bit affinity mask on pathological hosts.

Trust boundary

  • PROXY protocol: claimed loopback/unspecified sources are refused at the parser. The
    pp-claimed source was applied before the mandatory client-certificate gate, so on a
    pp-enabled public listener a direct client could claim 127.0.0.1 and be treated as a trusted
    internal peer: skipping the client certificate, reaching the loopback-gated cache purge/ready
    endpoints, and bypassing the plaintext mTLS-gate redirect. Such headers are malformed in both
    v1 and v2 (v4-mapped forms included) and the connection is closed. Real load balancers never
    relay loopback client addresses.
  • Rewrite [P] targets are screened away from the local trust plane. [P] URLs are
    request-influenceable (.htaccess authors; $N backrefs in the authority), and a same-host
    dial presents a loopback raw peer — exactly what the purge/ready endpoints, the metrics
    listener, and the admin listeners gate on. Rewrite-driven targets refuse unix:/uds:
    transports outright and resolve the authority (bounded 3 s) to reject loopback, unspecified,
    and link-local addresses — including /etc/hosts pins, which bypass literal screens.
    Config-declared proxy targets remain operator-trusted and unscreened.
  • CGI environment: directory-authored overrides of server-computed variables are denied
    (shared by LSAPI and FastCGI). Rewrite [E=...]/SetEnvIf entries could previously re-point
    SCRIPT_FILENAME at an arbitrary readable file or forge REMOTE_ADDR/HTTPS/SERVER_*.
    SCRIPT_NAME, QUERY_STRING, REDIRECT_* (front-controller and ErrorDocument contracts) and
    HTTP_* mirrors remain overridable.

Fail-closed defaults

  • .htaccess: unreadable or non-UTF-8 access files no longer degrade to "no rules." A
    present-but-unreadable file, or a single non-UTF-8 byte, memoized the directory as unprotected
    until the mtime changed. Non-UTF-8 files decode lossily (rules survive); any other read error
    yields the deny-all sentinel, self-healing on mtime change — matching the existing parse-error
    behavior.
  • lsphp: never-root is enforced on every credential path, and a setuid/setgid lsphp binary
    refuses the spawn outright (after the supervisor's credential drop, executing such a binary
    yields workers whose effective uid is root).
  • Isolation modes: out-of-range setUIDMode/chrootMode values fail the vhost load instead
    of silently disabling isolation.

Cache integrity

  • The page cache varies stored entries on Origin when the .htaccess chain reads it. An
    origin-reflecting chain (SetEnvIf / RewriteCond %{HTTP:Origin}, typically for a reflected
    Access-Control-Allow-Origin) produces origin-conditional responses, but the cache stored
    whatever headers the first storer's render produced — every later visitor received that
    request's CORS verdict. Origin now folds into the cache-key vary discriminant (bounded hash)
    across lookup, store, stale-if-error, and capsule keys. Absent/empty Origin keeps the
    historic base key, so entries persisted before this change stay hit-able; origin-bearing
    requests (browser fetch/XHR) re-render once per URL per origin after upgrading.

Admin write API and packaging

  • useIpInProxyHeader and the whole <security> block (trusted-CIDR access rules,
    followSymlink, geo/ASN lists) are restart-required rather than silently publishable as
    live "application" updates; unix-socket ext-processor addresses are authorized against
    --admin-resource-root.
  • The OCI seccomp profile gates ptrace/process_vm_* on CAP_SYS_PTRACE; the page-cache
    integrity key is created O_CREAT|O_EXCL at mode 0600 (no umask window, no symlink redirect).

New subsystems since the previous sync (all opt-in)

  • FastCGI gateway — bounded wire protocol, connection pool, CGI response parsing, streaming
    handler, and suffix routing.
  • WAF sidecar — request inspection before cache and backends with bounded aggregate memory;
    when configured, every request funnels through inspection (fast paths decline).
  • ACME client — durable HTTP-01 issuance/renewal with TLS activation, plus scoped DNS-01
    wildcard issuance with durable cleanup.
  • OCSP stapling — opt-in validated staple lifecycle with fail-closed "required" mode.
  • Admin APIs — read-only loopback introspection (GET /v1/status) and an opt-in write
    transaction API: token-file auth, bounded control requests, JSON config submissions validated
    against operator-declared resource roots, and revision-checked publications that leave the
    current configuration serving on any failure.
  • Transactional zero-downtime reload — configuration changes are classified; TLS/cert and
    trust-policy changes flow through prepared same-socket TCP successors with kernel-verified
    socket-identity handoff, raced-accept transfer, activation gating, and generation-pinned
    connection views. Failed candidates leave the currently serving generation untouched.
  • Reverse-proxy load-balancing groups — weighted selection, strict group configuration
    (canonical unix syntax, validated ports), bounded active health probes, and generation-scoped
    pool lifecycle.
  • OpenTelemetry — bounded OTLP trace/metric export with an explicit trust policy for inbound
    trace parents; spans cover H1/H2/H3, WebSocket relays, LSAPI dispatch, and cache/rewrite
    stages; export never blocks request handling.
  • kTLS — auto mode gated on real NIC TLS-hardware offload; H1 file bodies send via sendfile
    on plaintext and kTLS connections.
  • Logging — structured JSON error events with credential redaction, an optional syslog tap,
    and a shared parser corpus replay.
  • Bounded request decompression for Content-Encoding request bodies, and a hardened OCI
    packaging example.

Dependency refresh

All dependencies bumped to their latest releases, including major versions: rustls
0.23.45 (closes RUSTSEC-2026-0285)
, tokio 1.53.1, quinn 0.11.12, sha2 0.11, hmac 0.13,
zstd 0.14, brotli 9, base64 0.23, bcrypt 0.19.3. The vendored monoio fork was ported to
io-uring 0.7.15 (upstream monoio 0.2.4 remains the latest release): the fork's multishot
accept machinery re-applies unchanged on the 0.7 Builder<S, C> defaults, and the dependency
graph dedups to a single io-uring version. The minimum supported Rust is now 1.98.

Upgrade notes

  • PROXY-protocol listeners: connections whose PROXY header claims a loopback or unspecified
    source are now closed by design. If a fronting load balancer sends such headers, fix the LB.
  • Rewrite [P] rules targeting loopback, link-local, or unix sockets now return 403 — declare
    same-host backends as config proxy contexts instead; operator configuration is trusted,
    rewrite-driven targets are not.
  • [E=SCRIPT_FILENAME]-style env overrides of server-computed CGI variables are ignored;
    front-controller rewrites using SCRIPT_NAME/QUERY_STRING are unaffected.
  • setUIDMode/chrootMode values outside 0–2 abort the affected vhost config load instead of
    silently disabling isolation.
  • Everything else is behavior-identical for conformant clients; new subsystems activate only via
    their flags.

Validation

  • Full workspace test suite (16 crates, ~1,500 tests) green; new regression tests cover every
    security fix, including io_uring-driver tests that drive the real h2 deadline paths.
  • fast-memo parity gate: 192-case byte-identity matrix on a real 800-line .htaccess chain —
    zero differences.
  • Page-cache cross-URL, cross-scheme, and cross-host gates green; file-tier persistence gate
    restored 148k entries across a restart; private-tier session-isolation gate green.
  • A live probe on an origin-reflecting chain confirms per-origin CORS caching: a no-Origin
    request no longer replays the first storer's Access-Control-Allow-Origin.

Co-Authored-By: Claude Code noreply@anthropic.com