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.
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 | 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 | 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 |
|---|---|---|---|
| resolved (ADR-012) | |||
| resolved (ADR-007) | |||
| resolved (ADR-013) | |||
| resolved (ADR-014, superseded by ADR-028: authenticated HTTP admin API) | |||
| resolved (single bind_addr sufficient) | |||
| resolved (ADR-015) | |||
| resolved (ADR-019) | |||
/health use a less common path to avoid upstream collision? |
resolved (ADR-022: no /health route on main listener) |
||
upstream_connect_timeout_secs be enforced? |
resolved (ADR-026: 30s connector ceiling) | ||
| resolved (already specified in config.md; implementation bug C2) | |||
X-Forwarded-Proto be derived per-listener? |
resolved (hardcoded https is correct for TLS-terminating proxy) |
||
| 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 |
| 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 |