Task graph covers all Phase 1 concerns: config system, TLS termination, proxy handler, operations (rate limiting, logging, health check, admin socket, signals, shutdown, body size limit), deployment artifacts, and two review checkpoints. No circular dependencies. Critical path length of 7. Risk distribution: 3 high-risk (ACME, TLS listener setup, startup orchestration), 7 medium, 11 low, 2 trivial.
78 lines
3.2 KiB
Markdown
78 lines
3.2 KiB
Markdown
---
|
|
id: tls/manual-tls
|
|
name: Implement manual TLS certificate loading and ServerConfig construction
|
|
status: pending
|
|
depends_on: [setup/project-init]
|
|
scope: narrow
|
|
risk: low
|
|
impact: component
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
Implement the manual TLS mode where certificates are loaded from PEM files on disk at startup. This covers building a `rustls::ServerConfig` with manually loaded certificate chains and private keys.
|
|
|
|
### Manual Mode
|
|
|
|
For each listener in manual mode:
|
|
1. Load `cert_path` PEM file using `rustls_pemfile` → `Vec<CertificateDer>`
|
|
2. Load `key_path` PEM file using `rustls_pemfile` → `PrivateKeyDer`
|
|
3. Build `ServerConfig` with `with_no_client_auth()` and the loaded cert/key
|
|
4. Configure cipher suites (restricted set per ADR-012)
|
|
5. Configure protocol versions (TLS 1.2 and 1.3 only)
|
|
|
|
### Cipher Suite Configuration
|
|
|
|
Per ADR-012, restrict to nginx-equivalent cipher suites:
|
|
|
|
**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 via a custom `CryptoProvider` with a `cipher_suite` list passed to `ServerConfig::builder_with_provider()`.
|
|
|
|
### Single-Domain Manual Mode
|
|
|
|
For a listener with one domain, build a simple `ServerConfig` with the single certificate chain and private key. No SNI resolver needed.
|
|
|
|
### Multi-Domain Manual Mode (on shared-IP listener)
|
|
|
|
For a listener with multiple sites on a shared IP, implement a custom `ResolvesServerCert` that maps SNI hostnames to `CertifiedKey` entries loaded from disk. If no certificate matches the SNI hostname, the handshake fails — we don't serve a default certificate for unknown domains.
|
|
|
|
Note: multi-domain manual mode with different certs per domain is a rare edge case. The initial implementation should handle the common case (single cert per manual listener). The SNI resolver can be a follow-up if needed.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `rustls::ServerConfig` construction for manual TLS mode
|
|
- [ ] PEM file loading via `rustls_pemfile` for certificates and private keys
|
|
- [ ] Cipher suite restriction per ADR-012 (4 TLS 1.2 suites + all TLS 1.3)
|
|
- [ ] Protocol version restriction to TLS 1.2 and 1.3
|
|
- [ `aws_lc_rs` crypto provider used
|
|
- [ ] `with_no_client_auth()` for no client certificate requirement
|
|
- [ ] Custom `ResolvesServerCert` for SNI-based cert selection in multi-domain manual mode
|
|
- [ ] Unknown SNI hostname → handshake fails (no default cert)
|
|
- [ ] Unit tests for ServerConfig construction with test certs (using `rcgen`)
|
|
- [ ] Unit tests for cipher suite and protocol version configuration
|
|
|
|
## References
|
|
|
|
- docs/architecture/tls.md — manual mode, cipher suites, SNI
|
|
- docs/architecture/decisions/004-rustls-acme.md — manual mode is fallback
|
|
- docs/architecture/decisions/005-tokio-rustls-direct.md — direct tokio-rustls usage
|
|
- docs/architecture/decisions/012-cipher-suite-restriction.md — cipher suite selection
|
|
|
|
## Notes
|
|
|
|
> This task focuses on ServerConfig construction. The actual TCP listener + TLS acceptor wiring is in tls/tls-listener-setup.
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |