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

2.9 KiB

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