Expand architecture: multi-site Phase 1, multi-domain TLS, fix review issues
Promote multi-site support from Phase 2 to Phase 1 (ADR-010): the proxy must support git.alk.dev and alk.dev from initial release. Add multi-domain TLS configuration (ADR-011): acme_domains array replaces acme_domain string, single SAN certificate via rustls-acme. Key changes: - ADR-010: Multi-site in Phase 1 — avoids config format migration later - ADR-011: Multi-domain TLS — single SAN cert, acme_domains Vec<String> - ADR-002: Updated rationale for multi-site (one upstream per domain) - overview.md: Phase 1 now includes multi-site, alk.dev pass-through, dual licensing (MIT OR Apache-2.0), real IP removed - config.md: acme_domain → acme_domains, TOML example shows both sites, validation adds unique host check, real IP replaced with 203.0.113.10 - tls.md: Multi-domain SNI section moved from Future to current, manual mode uses ResolvesServerCert for SNI mapping, TOML header fixed - proxy.md: Updated for multi-site, removed single-domain language - operations.md: RFC 5737 documentation IPs, clarified rate limit eviction semantics (distinct scan interval vs eviction age) - open-questions.md: OQ-05 resolved (single bind_addr sufficient), new OQ-07 (per-site TLS overrides) Review fixes: - acme_domains (plural) consistently used across all docs and diagram - ADR-011 clearly scopes acme_domain as previous design - Inline decision rationale extracted: tls.md hot-reload → ADR-004 ref, config.md static/dynamic → ADR-008 ref - TOML section headers consistent (server.tls)
This commit is contained in:
92
docs/architecture/decisions/011-multi-domain-tls.md
Normal file
92
docs/architecture/decisions/011-multi-domain-tls.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# ADR-011: Multi-Domain TLS Configuration
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
With multi-site support in Phase 1 (ADR-010), the TLS configuration must
|
||||
support multiple domains. The previous design used a single `tls.acme_domain`
|
||||
string field, which only works for one domain.
|
||||
|
||||
There are several approaches to multi-domain TLS:
|
||||
|
||||
1. **Single ACME config with domain list**: `acme_domains = ["git.alk.dev",
|
||||
"alk.dev"]` — one certificate covering all domains (SAN certificate)
|
||||
2. **Per-site TLS configuration**: Each site entry specifies its own TLS
|
||||
mode (ACME or manual) and domain — more flexible but complex
|
||||
3. **Hybrid**: A global TLS section with ACME domains, plus per-site overrides
|
||||
for manual certificates
|
||||
|
||||
For our use case, all proxied domains use the same ACME certificate authority
|
||||
(Let's Encrypt) and the same challenge type (TLS-ALPN-01). There's no need
|
||||
for per-site TLS configuration in Phase 1.
|
||||
|
||||
## Decision
|
||||
|
||||
Use a single ACME configuration with a list of domains, producing one SAN
|
||||
certificate covering all proxied domains. Manual mode uses certificate file
|
||||
paths (single cert file with all domains, or one cert per domain resolved via
|
||||
SNI).
|
||||
|
||||
The config format changes from the previous single-domain format:
|
||||
|
||||
```toml
|
||||
# Previous (single-domain) format — no longer used
|
||||
[tls]
|
||||
mode = "acme"
|
||||
acme_domain = "git.alk.dev" # single string
|
||||
```
|
||||
|
||||
To the current multi-domain format:
|
||||
|
||||
```toml
|
||||
[tls]
|
||||
mode = "acme"
|
||||
acme_domains = ["git.alk.dev", "alk.dev"] # array of strings
|
||||
```
|
||||
|
||||
In ACME mode, `rustls-acme` provisions a single certificate covering all
|
||||
listed domains via Subject Alternative Names (SAN). This is the standard
|
||||
Let's Encrypt approach for multi-domain certificates.
|
||||
|
||||
In manual mode, the cert and key files must cover all domains (either a SAN
|
||||
certificate or separate certificates resolved via SNI).
|
||||
|
||||
## Rationale
|
||||
|
||||
- A single SAN certificate is simpler to manage (one renewal, one cert)
|
||||
- Let's Encrypt supports SAN certificates with up to 100 domains
|
||||
- `rustls-acme` accepts `Vec<String>` for domain lists — this is its natural
|
||||
API
|
||||
- All our domains use the same ACME configuration (Let's Encrypt production,
|
||||
TLS-ALPN-01 challenge)
|
||||
- Per-site TLS overrides add complexity with no current benefit
|
||||
- If per-site TLS configuration is needed later (e.g., a site with a manual
|
||||
cert), it can be added as an optional override without changing the global
|
||||
config structure
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive:**
|
||||
- Single certificate for all domains — simpler renewal, simpler cert management
|
||||
- Matches `rustls-acme`'s natural API (`AcmeConfig::new(domains: Vec<String>)`)
|
||||
- All domains in one cert means SNI resolution is handled by ACME automatically
|
||||
- Config format is a minimal change from single-domain
|
||||
|
||||
**Negative:**
|
||||
- Adding or removing a domain requires re-provisioning the certificate (ACME
|
||||
handles this automatically, but it means cert changes affect all domains)
|
||||
- If one domain fails ACME validation, the entire cert renewal fails (all
|
||||
domains must be validated) — mitigated by Let's Encrypt's domain-level
|
||||
validation
|
||||
- Per-site TLS configuration (e.g., a domain with a manual cert) requires a
|
||||
future config extension (OQ-07)
|
||||
|
||||
## References
|
||||
|
||||
- [tls.md](../tls.md)
|
||||
- [config.md](../config.md)
|
||||
- ADR-010 (multi-site in Phase 1)
|
||||
- ADR-004 (ACME-primary certificate management)
|
||||
Reference in New Issue
Block a user