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.
67 lines
2.5 KiB
Markdown
67 lines
2.5 KiB
Markdown
# ADR-004: ACME-Primary Certificate Management
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
The proxy needs TLS certificates for HTTPS. Two approaches are available:
|
|
|
|
1. **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.
|
|
|
|
2. **rustls-acme (built-in ACME client)**: The proxy handles ACME
|
|
certificate provisioning and renewal internally as a background task. No
|
|
external certbot dependency. The `ResolvesServerCertAcme` cert 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-acme` runs as a background tokio task that
|
|
handles certificate provisioning and renewal automatically (~30 days before
|
|
expiry).
|
|
- **No restart needed**: When `rustls-acme` provisions a new certificate, the
|
|
`ResolvesServerCertAcme` resolver updates atomically. No SIGHUP, no restart,
|
|
no file watching.
|
|
- **Proven pattern**: alknet uses the same approach successfully.
|
|
- **Cache persistence**: `DirCache` persists 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-acme` is 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](../tls.md)
|
|
- alknet ADR-008: ACME/Let's Encrypt decision
|
|
- `rustls-acme` crate: https://github.com/FlorianUekermann/rustls-acme |