Files
reverse-proxy/docs/architecture
glm-5.2 0885486028 Fix connection lifecycle, pool bounds, and log rotation (review #007)
Critical:
- C1: Set server-side idle/keep-alive timeouts on both hyper builders
  (http2 keep_alive_interval=15s + keep_alive_timeout, http1
  header_read_timeout). Both builders now set TokioTimer (required to
  avoid runtime panic). Prevents FD exhaustion from abandoned TLS
  connections — the root cause of the 2026-07-24 outage.
- C2: Add Semaphore(max_connections) gating the accept loop. Provides
  backpressure via OS TCP backlog when all permits are taken.

Warnings:
- W1: Add SIGUSR1 log-reopen handler. New ReopenableFileWriter
  (Arc<ArcSwap<File>> via custom MakeWriter) atomically swaps the log
  file. Enables postrotate logrotate without copytruncate, which caused
  the 1.15GB sparse file that wedged fail2ban.
- W2: Set pool_max_idle_per_host(10) on both upstream clients, bounding
  idle upstream connections per host.
- W3: Add connection_idle_timeout_secs to StaticConfig (default 60).
- W4: Add max_connections to StaticConfig (default 1024).

Both new fields are validated (> 0) and included in static config drift
detection on reload. Docs (config.md, README, ADR-009) updated.
2026-07-28 10:16:26 +00:00
..

status, last_updated
status last_updated
draft 2026-06-14

Reverse Proxy — Architecture

Current State

Phase 1 (Implementation) — Complete. The proxy is deployed and running in a Docker container, replacing our vulnerable nginx 1.24.0 installation.

This project replaces our vulnerable nginx 1.24.0 installation with a memory-safe Rust/axum reverse proxy. The primary motivation is CVE-2026-42945 (unauthenticated RCE in nginx's rewrite module) and the broader pattern of memory corruption bugs in nginx's C codebase.

The proxy supports multiple domains from initial release (git.alk.dev and alk.dev), with per-domain host-based routing and a single multi-domain SAN certificate via ACME. HTTP/2 is supported on the client-facing side (between the client and the proxy) with ALPN-based protocol detection. Upstream connections remain HTTP/1.1.

Architecture Documents

Document Status Description
overview.md Draft Vision, scope, crate dependencies, exports
proxy.md Draft Reverse proxy handler, request flow, header injection
tls.md Reviewed TLS termination, ACME, manual certs, SNI, ALPN
config.md Draft TOML config format, static/dynamic split, ArcSwap reload
operations.md Draft Rate limiting, logging, health check, systemd, shutdown

ADR Table

ADR Title Status
001 Rust with Axum Accepted
002 Custom Proxy Handler Accepted
003 TOML Configuration Format Accepted
004 ACME-Primary Certificate Management Accepted
005 tokio-rustls Directly, Not axum-server Accepted
006 Token Bucket Rate Limiting Accepted
007 Custom Structured Log Format Accepted
008 Static/Dynamic Config Split with ArcSwap Accepted
009 Signal Handling Strategy Accepted
010 Multi-Site Support in Phase 1 Accepted
011 Multi-Domain TLS Configuration Accepted
012 Restrict Cipher Suites to nginx Scope Accepted
013 Health Check on Separate Local Port Accepted
014 Unix Domain Socket Config Reload API Superseded by ADR-028
015 Per-Site Upstream Timeouts with Defaults Accepted
016 Explicit Bind Address Requirement Accepted
017 Upstream Connection Defaults Accepted
018 Request Body Size Limit Accepted
019 Multi-Config Listener Support Accepted
020 Container Deployment Model Accepted
021 X-Forwarded-For Edge Proxy Model Accepted
022 Health Check Scope — Local Port and Admin HTTP Only Accepted
023 HTTP/2 Client-Facing Support Accepted
024 ANSI-Disabled Logging for Container Deployments Accepted
025 Rate Limiter IP Source — ConnectInfo Only Accepted
026 Connector Timeout Ceiling for Per-Site Timeouts Accepted
027 Admin Socket Resource Limits Deprecated (ADR-028)
028 Authenticated HTTP Admin API Accepted
029 Config File TOCTOU Mitigation on Reload Accepted
030 Store cli_allow_wildcard_bind in ConfigReloadHandle Accepted

Open Questions

See open-questions.md for the full tracker.

OQ Question Priority Status
OQ-01 Should cipher suites be restricted beyond rustls defaults? medium resolved (ADR-012)
OQ-02 What log format should fail2ban consume? high resolved (ADR-007)
OQ-03 Should the health check endpoint be on a separate port? low resolved (ADR-013)
OQ-04 Config reload: SIGHUP only or also Unix socket API? low resolved (ADR-014, superseded by ADR-028: authenticated HTTP admin API)
OQ-05 Should the proxy bind to multiple addresses? low resolved (single bind_addr sufficient)
OQ-06 Should upstream timeouts be configurable per-site? low resolved (ADR-015)
OQ-07 Should per-site TLS overrides be supported for mixed ACME/manual domains? low resolved (ADR-019)
OQ-08 Should /health use a less common path to avoid upstream collision? medium resolved (ADR-022: no /health route on main listener)
OQ-09 How should upstream_connect_timeout_secs be enforced? medium resolved (ADR-026: 30s connector ceiling)
OQ-10 Should ACME contact email be a required config field? high resolved (already specified in config.md; implementation bug C2)
OQ-11 How should X-Forwarded-Proto be derived per-listener? medium resolved (hardcoded https is correct for TLS-terminating proxy)
OQ-12 Should request access logging be mandatory or optional? high resolved (mandatory, always-on per operations.md)
OQ-13 Should acme_contact support multiple email addresses? low open
OQ-14 Should rate limiter eviction interval and max age be configurable? low open
OQ-15 Should admin key rotation persist across restarts? medium open
OQ-16 Should /admin/reload use POST instead of GET? low resolved (ADR-028: POST for state-changing, GET for read-only)

Document Lifecycle

Status Meaning Transitions
draft Under active development. May change significantly. reviewed when open questions are resolved
reviewed Architecture is final. Implementation may begin. Changes require review. stable when implementation is complete
stable Locked. Changes require review and may warrant an ADR. deprecated when superseded
deprecated Superseded. Kept for reference. Removed when no longer referenced