docs: implementation plan for gateway TLS auto-cert and lenient client trust
This commit is contained in:
@@ -50,9 +50,12 @@ New type `SelfSignedCertificateProvider` in
|
||||
day` (clock-skew slack), `notAfter = now + ValidityYears`. SANs: `DNS=localhost`,
|
||||
`DNS=<MachineName>`, `DNS=<MachineName.FQDN>` when resolvable, plus
|
||||
`IP=127.0.0.1` and `IP=::1`. Server-auth EKU.
|
||||
4. **Persist securely.** Write the PFX with a random in-memory-only export password;
|
||||
restrictive ACL (SYSTEM + Administrators + service account) on the `certs`
|
||||
directory and file; atomic write (temp + rename).
|
||||
4. **Persist securely.** Write the PFX with an **empty** export password (a random
|
||||
in-memory password cannot be reused across restarts, which the persist-and-reuse
|
||||
decision requires); protect the private key with a restrictive ACL (SYSTEM +
|
||||
Administrators + service account) on the `certs` directory and file on Windows,
|
||||
and `0600` on non-Windows; atomic write (temp + rename). After generating, the
|
||||
cert is reloaded from the persisted PFX so Kestrel always serves the on-disk key.
|
||||
5. **Wire into Kestrel.** In `GatewayApplication.CreateBuilder`, add
|
||||
`builder.WebHost.ConfigureKestrel(o => o.ConfigureHttpsDefaults(h =>
|
||||
h.ServerCertificate = cert))`. `ConfigureHttpsDefaults` supplies the cert only
|
||||
@@ -71,8 +74,11 @@ All optional; the zero-config path needs none of them.
|
||||
| `Tls:AdditionalDnsNames` | `[]` | Extra SANs (e.g. a load-balancer name) |
|
||||
| `Tls:RegenerateIfExpired` | `true` | Auto-replace an expired persisted cert |
|
||||
|
||||
Validated by `GatewayOptionsValidator`: path non-empty when TLS is active,
|
||||
`ValidityYears` in 1–100.
|
||||
Validated by `GatewayOptionsValidator`: `ValidityYears` in 1–100,
|
||||
`SelfSignedCertPath` is a valid path shape when non-blank, and
|
||||
`AdditionalDnsNames` entries are non-blank. (The "https endpoint exists but cert
|
||||
path is blank" fail-fast lives in the bootstrap/provider, not the validator,
|
||||
because the validator only sees the `MxGateway` section, not `Kestrel:Endpoints`.)
|
||||
|
||||
**Logging:** on generate/load, log thumbprint + SAN list + `notAfter` at
|
||||
Information. Never log the PFX password or private key.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"planPath": "docs/plans/2026-06-01-gateway-cert-autogen-implementation.md",
|
||||
"tasks": [
|
||||
{"id": 1, "subject": "Task 1: Add TlsOptions config + bind into GatewayOptions", "status": "pending"},
|
||||
{"id": 2, "subject": "Task 2: Validate MxGateway:Tls in GatewayOptionsValidator", "status": "pending", "blockedBy": [1]},
|
||||
{"id": 3, "subject": "Task 3: SelfSignedCertificateProvider.GenerateCertificate", "status": "pending", "blockedBy": [1]},
|
||||
{"id": 4, "subject": "Task 4: SelfSignedCertificateProvider.LoadOrCreate (persist/reuse/regenerate/ACL)", "status": "pending", "blockedBy": [3]},
|
||||
{"id": 5, "subject": "Task 5: KestrelTlsInspector (detect HTTPS-without-cert)", "status": "pending"},
|
||||
{"id": 6, "subject": "Task 6: Wire auto-cert into GatewayApplication.CreateBuilder", "status": "pending", "blockedBy": [1, 4, 5]},
|
||||
{"id": 7, "subject": "Task 7: .NET client lenient TLS by default", "status": "pending"},
|
||||
{"id": 8, "subject": "Task 8: Go client lenient TLS by default", "status": "pending"},
|
||||
{"id": 9, "subject": "Task 9: Java client lenient TLS by default", "status": "pending"},
|
||||
{"id": 10, "subject": "Task 10: Python client lenient TLS via TOFU pre-fetch", "status": "pending"},
|
||||
{"id": 11, "subject": "Task 11: Rust client lenient TLS via rustls verifier (spike + fallback)", "status": "pending"},
|
||||
{"id": 12, "subject": "Task 12: Documentation", "status": "pending", "blockedBy": [6, 7, 8, 9, 10, 11]}
|
||||
],
|
||||
"lastUpdated": "2026-06-01"
|
||||
}
|
||||
Reference in New Issue
Block a user