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.
3.2 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| tls/manual-tls | Implement manual TLS certificate loading and ServerConfig construction | pending |
|
narrow | low | component | 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:
- Load
cert_pathPEM file usingrustls_pemfile→Vec<CertificateDer> - Load
key_pathPEM file usingrustls_pemfile→PrivateKeyDer - Build
ServerConfigwithwith_no_client_auth()and the loaded cert/key - Configure cipher suites (restricted set per ADR-012)
- 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_SHA256TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS 1.3 (all default suites):
TLS_AES_128_GCM_SHA256TLS_AES_256_GCM_SHA384TLS_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::ServerConfigconstruction for manual TLS mode- PEM file loading via
rustls_pemfilefor 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_rscrypto provider used with_no_client_auth()for no client certificate requirement- Custom
ResolvesServerCertfor 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