docs(rv2-tls): lock two more F3c-net requirements from the gofiber/Go comparison
Compared §F3c-net's forks against gofiber v3's client (fasthttp + Go crypto/tls/x509, .dev/reference/fiber). Two gaps my defaults had vs Go, now locked as decisions 5 and 6: - (5) bounded handshake deadline: the blocking model would let a stalled server hang the shard's one thread indefinitely (the DoS DoTimeout closes). connect_tls now bounds connect+handshake via non-blocking connect+poll + SO_RCVTIMEO/SNDTIMEO, default WO_TLS_HANDSHAKE_MS (10s); expiry traps WO_T_IO. _dl variant + park handshake stay follow-ups - (6) chain hardening: signatures+validity+SAN alone let a leaf act as a CA. Now every non-leaf must assert basicConstraints CA:TRUE (+pathLen) and the leaf must carry EKU serverAuth — what Go's crypto/x509 enforces - acceptance criteria added (stalled-server timeout; leaf-as-CA + no-EKU rejected); connect_tls bullet, frontmatter review_pending, status NEXT PLAN updated to six locked forks Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 9662cd8b040f417b4886dae9ce97b70083e24e40)
This commit is contained in:
parent
f1e355c4d4
commit
53485a748c
2 changed files with 48 additions and 13 deletions
|
|
@ -95,12 +95,16 @@ developer second review before this drives a live connection.
|
||||||
|
|
||||||
**Next step — BUILD F3c-net (the only remaining rung before jarvis unblocks).**
|
**Next step — BUILD F3c-net (the only remaining rung before jarvis unblocks).**
|
||||||
Its spec is now `ready`: [rv2 9 §F3c-net](runtime-v2/09-in-process-tls.md)
|
Its spec is now `ready`: [rv2 9 §F3c-net](runtime-v2/09-in-process-tls.md)
|
||||||
brainstormed 2026-09-09 with the four integration forks **locked** (grounded in
|
brainstormed 2026-09-09 with **six** integration forks **locked** (four grounded
|
||||||
the runtime, not assumed): (1) blocking connect+handshake then park the data
|
in the runtime, two from a gofiber/Go `crypto/x509` comparison): (1) blocking
|
||||||
plane, mirroring `net.connect` — park-based handshake a named follow-up; (2) a
|
connect+handshake then park the data plane, mirroring `net.connect` — park-based
|
||||||
per-shard fd-keyed `wo_tls_conn` slot table, no locks (the `wo_child` pattern);
|
handshake a named follow-up; (2) a per-shard fd-keyed `wo_tls_conn` slot table,
|
||||||
(3) failures trap `WO_T_IO` loudly incl. chain/hostname; (4) per-shard lazy
|
no locks (the `wo_child` pattern); (3) failures trap `WO_T_IO` loudly incl.
|
||||||
read-only CA bundle (`/etc/ssl/certs/…`, `WO_CA_BUNDLE` override). Builtins:
|
chain/hostname; (4) per-shard lazy read-only CA bundle (`/etc/ssl/certs/…`,
|
||||||
|
`WO_CA_BUNDLE` override); (5) a **bounded handshake deadline**
|
||||||
|
(`WO_TLS_HANDSHAKE_MS`, non-blocking connect+poll + `SO_RCVTIMEO/SNDTIMEO`) so a
|
||||||
|
stalled server can't hang the shard; (6) **chain hardening** — basicConstraints
|
||||||
|
CA:TRUE + pathLen + leaf EKU `serverAuth`, not just signatures. Builtins:
|
||||||
`net.connect_tls`/`read_tls`/`write_tls` (ids 115–117, `WO_B_MAX`→117), wiring
|
`net.connect_tls`/`read_tls`/`write_tls` (ids 115–117, `WO_B_MAX`→117), wiring
|
||||||
across `wob.h`/`emit.ml`/`types.ml`/`loader.c`/`builtin.c`/`sysio.c`, live-gated
|
across `wob.h`/`emit.ml`/`types.ml`/`loader.c`/`builtin.c`/`sysio.c`, live-gated
|
||||||
against a local TLS server. Then **G** (inbound server) for porch; after that
|
against a local TLS server. Then **G** (inbound server) for porch; after that
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ track: runtime-v2
|
||||||
iteration: "9"
|
iteration: "9"
|
||||||
status: in-progress
|
status: in-progress
|
||||||
readiness: ready
|
readiness: ready
|
||||||
review_pending: "forks auto-approved 2026-09-08/09 for autonomous execution — developer second review before this ships. Landed + KAT'd (RFC 8448 / real certs): A–E crypto, F1 record, F2 key schedule, F3a messages, F3b offline verify, F3c-core sans-io driver, SAN/hostname, F3c-net chain validation. §F3c-net (socket/VM slice) brainstormed to READY 2026-09-09 with the four integration forks locked (blocking connect+handshake then park data I/O; per-shard fd-keyed slot table no-locks; failures trap WO_T_IO; per-shard lazy read-only CA bundle). Remaining to BUILD: F3c-net (net.connect_tls/read_tls/write_tls, ids 115-117, live-gated), then G inbound server"
|
review_pending: "forks auto-approved 2026-09-08/09 for autonomous execution — developer second review before this ships. Landed + KAT'd (RFC 8448 / real certs): A–E crypto, F1 record, F2 key schedule, F3a messages, F3b offline verify, F3c-core sans-io driver, SAN/hostname, F3c-net chain validation. §F3c-net (socket/VM slice) brainstormed to READY 2026-09-09 with six integration forks locked (blocking connect+handshake then park data I/O; per-shard fd-keyed slot table no-locks; failures trap WO_T_IO; per-shard lazy read-only CA bundle; a bounded handshake deadline WO_TLS_HANDSHAKE_MS; chain hardening = basicConstraints CA:TRUE + EKU serverAuth — the last two added from the gofiber/Go crypto/x509 comparison). Remaining to BUILD: F3c-net (net.connect_tls/read_tls/write_tls, ids 115-117, live-gated), then G inbound server"
|
||||||
---
|
---
|
||||||
|
|
||||||
# runtime-v2 9 — in-process TLS: retiring the proxy-termination doctrine
|
# runtime-v2 9 — in-process TLS: retiring the proxy-termination doctrine
|
||||||
|
|
@ -86,13 +86,16 @@ they may split into their own runtime-v2 iterations as they are picked up.
|
||||||
|
|
||||||
Everything security-critical is landed and offline-KAT'd. What is left is I/O
|
Everything security-critical is landed and offline-KAT'd. What is left is I/O
|
||||||
integration that can only be gated **live** (a local `openssl s_server` / python
|
integration that can only be gated **live** (a local `openssl s_server` / python
|
||||||
TLS server), so it is one cohesive slice, not further split. The four
|
TLS server), so it is one cohesive slice, not further split. The integration
|
||||||
integration forks are settled below, each grounded in the existing runtime, not
|
forks are settled below — the first four grounded in the existing runtime, the
|
||||||
assumed. This section is `ready`: the decisions are locked, the acceptance
|
last two added 2026-09-09 from a comparison against **gofiber v3's client**
|
||||||
|
(fasthttp + Go `crypto/tls`/`crypto/x509`, in `.dev/reference/fiber`), which
|
||||||
|
bounds every request with a timeout and delegates full chain checks to
|
||||||
|
`crypto/x509`. This section is `ready`: the decisions are locked, the acceptance
|
||||||
criteria are stated, and code may start once a developer signs off the
|
criteria are stated, and code may start once a developer signs off the
|
||||||
`review_pending` marker.
|
`review_pending` marker.
|
||||||
|
|
||||||
### The four decisions, locked
|
### The locked decisions
|
||||||
|
|
||||||
1. **Blocking connect + blocking handshake, then park the data plane.** This
|
1. **Blocking connect + blocking handshake, then park the data plane.** This
|
||||||
mirrors `net.connect` exactly (`sysio.c` `WO_B_NET_CONNECT`): the socket is
|
mirrors `net.connect` exactly (`sysio.c` `WO_B_NET_CONNECT`): the socket is
|
||||||
|
|
@ -128,6 +131,24 @@ criteria are stated, and code may start once a developer signs off the
|
||||||
with (2). Path: `/etc/ssl/certs/ca-certificates.crt` (confirmed present on the
|
with (2). Path: `/etc/ssl/certs/ca-certificates.crt` (confirmed present on the
|
||||||
dev box), overridable by the `WO_CA_BUNDLE` environment variable — which is
|
dev box), overridable by the `WO_CA_BUNDLE` environment variable — which is
|
||||||
also how the live gate points the client at its self-signed test CA.
|
also how the live gate points the client at its self-signed test CA.
|
||||||
|
5. **A bounded handshake deadline (no unbounded shard stall).** The blocking
|
||||||
|
model of decision (1) would otherwise let a slow or hostile server stall the
|
||||||
|
shard's one thread indefinitely during connect + handshake — the DoS that
|
||||||
|
gofiber closes with `DoTimeout`. So `net.connect_tls` bounds the whole
|
||||||
|
connect+handshake by a deadline: **non-blocking `connect()` + `poll` for the
|
||||||
|
TCP step, and `SO_RCVTIMEO`/`SO_SNDTIMEO` on the blocking socket across the
|
||||||
|
handshake**, capping the stall without needing the full park refit. Default
|
||||||
|
from `WO_TLS_HANDSHAKE_MS` (10 000 ms if unset); expiry aborts and traps
|
||||||
|
`WO_T_IO` ("tls: handshake timeout"). A per-call `_dl` variant and the
|
||||||
|
park-based handshake remain the named follow-ups.
|
||||||
|
6. **Chain hardening: basicConstraints + EKU (not just signatures).** Signature
|
||||||
|
+ validity + SAN is not enough — Go's `crypto/x509` also enforces the
|
||||||
|
constraints that stop a leaf from masquerading as a CA. So the phase-E
|
||||||
|
extension walk and `wo_tls_verify_chain` gain: every **non-leaf** cert must
|
||||||
|
assert `basicConstraints` CA:TRUE and satisfy `pathLenConstraint`, and the
|
||||||
|
**leaf** must carry Extended Key Usage `id-kp-serverAuth` (or omit EKU
|
||||||
|
entirely). A `keyUsage` `keyCertSign` check on issuers is included where
|
||||||
|
present. Failure is a rejection like any other chain fault (no partial trust).
|
||||||
|
|
||||||
### The builtin surface
|
### The builtin surface
|
||||||
|
|
||||||
|
|
@ -139,8 +160,10 @@ Three new builtins on the `net` module (one numeric id space; `WO_B_MAX` moves
|
||||||
X25519 key + ClientHello random/session-id, run the sans-io driver over the
|
X25519 key + ClientHello random/session-id, run the sans-io driver over the
|
||||||
blocking socket (frame each record: read the 5-byte header, then the body;
|
blocking socket (frame each record: read the 5-byte header, then the body;
|
||||||
flush `take_output`) to ESTABLISHED, set the host on the driver so the leaf
|
flush `take_output`) to ESTABLISHED, set the host on the driver so the leaf
|
||||||
SAN is enforced, then `wo_tls_verify_chain` against the lazily-loaded anchors.
|
SAN is enforced, then `wo_tls_verify_chain` against the lazily-loaded anchors
|
||||||
Returns the fd (a slot is claimed for it); traps on any failure.
|
(with the decision-6 basicConstraints/EKU checks). The whole connect+handshake
|
||||||
|
is bounded by the decision-5 deadline. Returns the fd (a slot is claimed for
|
||||||
|
it); traps on any failure.
|
||||||
- `net.read_tls(fd, max) -> Bytes` — id **116**, arity 2. Reads/decrypts one
|
- `net.read_tls(fd, max) -> Bytes` — id **116**, arity 2. Reads/decrypts one
|
||||||
application record via the slot, returning up to `max` plaintext bytes (EOF is
|
application record via the slot, returning up to `max` plaintext bytes (EOF is
|
||||||
the empty Bytes), buffering a partial record and parking on POLLIN, and
|
the empty Bytes), buffering a partial record and parking on POLLIN, and
|
||||||
|
|
@ -165,6 +188,14 @@ consumer change beyond the id additions.
|
||||||
- **Given** a server whose certificate does not chain to a trusted anchor, whose
|
- **Given** a server whose certificate does not chain to a trusted anchor, whose
|
||||||
SAN does not match the host, or is expired, **when** `net.connect_tls` runs,
|
SAN does not match the host, or is expired, **when** `net.connect_tls` runs,
|
||||||
**then** it traps `WO_T_IO` — no connection is returned.
|
**then** it traps `WO_T_IO` — no connection is returned.
|
||||||
|
- **Given** a server that accepts the TCP connection but then stalls (never
|
||||||
|
finishing the handshake), **when** the decision-5 deadline elapses,
|
||||||
|
**then** `net.connect_tls` aborts and traps `WO_T_IO` rather than stalling the
|
||||||
|
shard indefinitely — proven with a stub that connects then sleeps.
|
||||||
|
- **Given** a chain whose issuer lacks `basicConstraints` CA:TRUE (a leaf used
|
||||||
|
to sign another cert), or a leaf lacking EKU `serverAuth`, **when**
|
||||||
|
`net.connect_tls` validates it, **then** it is rejected — with negative KATs
|
||||||
|
in `test_tls` alongside the existing chain cases.
|
||||||
- **Given** two shards each dialing TLS, **when** they run concurrently, **then**
|
- **Given** two shards each dialing TLS, **when** they run concurrently, **then**
|
||||||
neither reads the other's slot or bundle (per-shard, no locks), proven under
|
neither reads the other's slot or bundle (per-shard, no locks), proven under
|
||||||
ASan/TSan.
|
ASan/TSan.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue