Files
reverse-proxy/docs/architecture/decisions/012-cipher-suite-restriction.md
glm-5.1 9a2352e61c Resolve 5 open questions, add 7 ADRs for previously undocumented decisions
Resolve open questions:
- OQ-01: Restrict cipher suites to match nginx scope (4 ECDHE-AES-GCM
  suites for TLS 1.2 + all TLS 1.3 suites) — ADR-012
- OQ-03: Health check on separate local port (default 9900, localhost
  only) — ADR-013
- OQ-04: Add Unix domain socket admin API for config reload alongside
  SIGHUP, with structured success/failure responses — ADR-014
- OQ-06: Per-site upstream timeouts with defaults (5s connect, 60s
  request), overridable in SiteConfig — ADR-015

Document previously undocumented decisions flagged by architecture review:
- ADR-016: Explicit bind address requirement (reject 0.0.0.0)
- ADR-017: Upstream connection defaults (HTTP/1.1, no redirects, pooling)
- ADR-018: 100 MB body size limit (matches nginx, Gitea compatibility)

OQ-07 (per-site TLS overrides) remains open for future consideration.

Spec updates:
- config.md: add health_check_port, admin_socket_path, per-site timeout
  fields, update TOML example and validation rules
- proxy.md: reference ADR-015/017/018 for timeouts, connection defaults,
  and body limit decisions
- tls.md: replace OQ-01 cipher suite section with ADR-012 decision
- operations.md: add local health check port section, admin socket reload
- overview.md: update Phase 1 scope with new features, add ADR references
- open-questions.md: resolve OQ-01/03/04/06, keep OQ-07 open
2026-06-11 09:07:36 +00:00

83 lines
2.9 KiB
Markdown

# ADR-012: Restrict Cipher Suites to Match nginx Scope
## Status
Accepted
## Context
Our current nginx configuration explicitly restricts cipher suites to four
ECDHE-AES-GCM suites:
```
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
```
rustls 0.23 with the `aws_lc_rs` crypto provider defaults to a conservative
set that excludes all weak ciphers (no SHA-1, no 3DES, no RC4, no CBC-mode
suites, no RSA key exchange). The rustls defaults include these four suites
plus TLS 1.3 suites (which nginx also allows via `TLSv1.3`).
The question was whether to accept the wider rustls defaults or restrict to
the same scope as our current nginx configuration (see OQ-01).
## Decision
Explicitly restrict cipher suites to match the same scope as our current nginx
configuration: the four ECDHE-AES-GCM suites for TLS 1.2, plus all TLS 1.3
suites. This is slightly more restrictive than rustls defaults but preserves
compatibility with all modern clients.
The restricted set in rustls terms:
**TLS 1.2 (explicitly selected):**
- `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`
- `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`
- `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`
- `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`
**TLS 1.3 (all default suites):**
- `TLS_AES_128_GCM_SHA256`
- `TLS_AES_256_GCM_SHA384`
- `TLS_CHACHA20_POLY1305_SHA256`
This is configured by building a `CryptoProvider` with a custom
`cipher_suite` list and passing it to `ServerConfig::builder_with_provider()`.
## Rationale
- Maintains behavioral parity with our current nginx configuration during
migration — no client that worked with nginx should see different TLS
behavior
- The four TLS 1.2 suites are the same ones nginx allows, providing a known
security baseline
- TLS 1.3 suites are all modern and secure — restricting them provides no
meaningful security benefit and reduces compatibility
- rustls defaults include ChaCha20-Poly1305 suites for TLS 1.2 which nginx does
not; these are cryptographically sound but represent a wider scope than our
current nginx config allows
- Explicit configuration means the cipher list is documented and auditable
- If compatibility issues arise, expanding the list is straightforward; the
reverse (restricting after deployment) risks breaking existing clients
## Consequences
**Positive:**
- Behavioral parity with current nginx TLS configuration
- Explicitly auditable cipher list
- No client that currently works will see different TLS behavior
- Matches security review expectations
**Negative:**
- Slightly more restrictive than rustls defaults — excludes ChaCha20-Poly1305
for TLS 1.2 and AES-CCM suites (rarely used)
- Must update the cipher list when deprecating TLS 1.2 in the future
- Custom `CryptoProvider` construction is slightly more code than using defaults
## References
- [tls.md](../tls.md)
- OQ-01 (now resolved)