Phase 1 architecture docs covering proxy handler, TLS termination (ACME + manual), TOML config with static/dynamic split (ArcSwap), and operations (rate limiting, logging, health check, systemd, graceful shutdown). Nine ADRs documenting key decisions: Rust/axum, custom proxy handler, TOML config, rustls-acme for cert management, tokio-rustls direct, token bucket rate limiting, custom log format for fail2ban, static/dynamic config split, and signal handling strategy. Includes threat landscape research documenting the nginx CVEs motivating this project.
2.5 KiB
ADR-004: ACME-Primary Certificate Management
Status
Accepted
Context
The proxy needs TLS certificates for HTTPS. Two approaches are available:
-
certbot (external ACME client): Run certbot as a cron job or systemd timer to obtain and renew certificates. The proxy loads certificates from files on disk. Renewal requires either SIGHUP/restart or inotify file watching to pick up new certs.
-
rustls-acme (built-in ACME client): The proxy handles ACME certificate provisioning and renewal internally as a background task. No external certbot dependency. The
ResolvesServerCertAcmecert resolver automatically serves the correct certificate and updates when renewed.
The alknet project has successfully implemented the rustls-acme approach, and its patterns are directly reusable.
Decision
Use rustls-acme as the primary certificate management mode, with manual
certificate paths as a fallback mode for testing, self-signed certs, and
corporate CA environments.
Rationale
- Eliminates certbot dependency: No external cron job, no deploy hooks, no certbot package to install and maintain. The proxy is self-contained.
- Automatic renewal:
rustls-acmeruns as a background tokio task that handles certificate provisioning and renewal automatically (~30 days before expiry). - No restart needed: When
rustls-acmeprovisions a new certificate, theResolvesServerCertAcmeresolver updates atomically. No SIGHUP, no restart, no file watching. - Proven pattern: alknet uses the same approach successfully.
- Cache persistence:
DirCachepersists ACME state between restarts, avoiding re-provisioning. - Fallback mode: Manual cert paths are still supported for environments where ACME is not possible.
Consequences
Positive:
- Single binary deployment (no certbot dependency)
- Zero-downtime certificate renewal
- Simpler operational model (no certbot cron, no deploy hooks)
- Proven in alknet
Negative:
rustls-acmeis an additional dependency- ACME challenges require either port 80 (HTTP-01) or TLS-ALPN-01 on port 443, which our proxy already listens on
- Less control over certificate issuance compared to certbot (e.g., no DNS-01 challenge support, though rustls-acme supports TLS-ALPN-01 which is sufficient for our use case)
- Manual mode requires restart for cert changes (acceptable for fallback)
References
- tls.md
- alknet ADR-008: ACME/Let's Encrypt decision
rustls-acmecrate: https://github.com/FlorianUekermann/rustls-acme