fix: session-lifecycle hardening — path-switch re-handshake (#36) + cert revocation (#41)#95
Merged
Merged
Conversation
#36: preserve the in-flight Noise ephemeral across a path re-target (resend the existing init_pkt instead of begin_handshake minting a fresh one) so a responder already Established on the old path completes us via its cached_resp — closes the path-switch half-open black hole without touching the anti-hijack gate or needing #34. #41: re-verify the peer's cert in a received rekey Init (drop the session on failure) + a periodic cert-liveness sweep on Established mesh peers (roots exempt) so a revoked/expired member's session drops within a rekey interval rather than at process restart. Both no-ops when membership is off / no handshake in flight; no wire change.
… + #41) 4 tasks, subagent-driven, TDD: (1) #36 retarget_handshake helper + wire the two tick escalation arms (preserve the ephemeral, don't mint fresh); (2) #41 drop_session + re-verify cert on rekey Init; (3) #41 member_cert_valid sweep (roots exempt, throttled); (4) netns money tests both drivers + CI.
…line punch->relay scenario) Ephemeral preservation (Task 1) fixes A's side but #91's path-consistency gate blocks B's: a direct-established responder drops A's relayed cold-start Init. Add one adoption case — a relayed cold-start RETRANSMIT (init_eph == cached_resp_init_eph) against a relay==false peer adopts relay for egress + replays cached_resp over the relay. New-ephemeral relayed Inits vs a direct peer stay #91 fail-closed. Accepted tradeoff: an attacker replaying a captured Init forces a direct->relay path downgrade (data still reaches the real peer; rides with #34).
…reset, spec tradeoff precision Review of the complete #36 fix (APPROVED, 2 Important + 2 Minor, non-blocking): - Important: update Peer.relay field doc for the #36 Established-adoption exception. - Important: spec tradeoff prose now states the forced downgrade is persistent for the session AND degrades direct rekey (rides with #34). - Minor: retarget_handshake resets last_sent_ms=now so no immediate redundant retransmit (anti-DPI timing); test asserts it. - Minor: spec retarget behavior now documents the endpoint clear/re-stamp asymmetry.
…eers + accurate recovery bound Task 3 review Minor: member_cert_valid is directory-keyed (populated only by gossip ingest_record, not admit_member), so a validly cold-start-admitted peer whose Record hasn't gossiped in can be swept. Bounded/recoverable (re-admission re-verifies the presented cert), but recovery is ~rekey_interval/2 via the responder's accept_rekey_init age gate, not instant.
…cert revocation yip-ca sign-cert gains --secs N (sub-day validity) for minting short-lived certs in the revocation test. Two netns money tests (both drivers, CI-wired): - run-netns-pathswitch-rehandshake.sh (#36): A escalates punch->relay while B is direct-established; asserts A converges over the relay carrying the SAME ephemeral (DISTINCT_INIT_EPHEMERALS=1, no cold-start churn) + relay-forwarded>0. - run-netns-cert-revocation.sh (#41): A's short-lived cert expires; asserts B drops A's session within a bounded window AND A cannot re-establish (re-admission gate refuses the expired cert). All four runs (2 tests x poll+uring) pass under sudo.
…certs too; roots exempted by caller) Final-review Minor: the doc had an orphaned handle_handshake_init block + a 'responder's msg2' framing that this branch's new #41 call sites (initiator msg1 cert, is_root-exempt) made inaccurate.
…(CI fix) CI netns-tunnel-test failed: the #41 test (a) waited out the daemon's 300s CLOCK_SKEW grace (>5min) and (b) raced mesh discovery with a single establish ping that didn't converge on the slower CI runner. - membership.rs: add YIP_CERT_SKEW_SECS env override (default 300, like YIP_REKEY_INTERVAL_MS) so a cert can expire in seconds for the test; prod leaves it unset. Cached via OnceLock; 236 unit tests green at the default. - run-netns-cert-revocation.sh: set YIP_CERT_SKEW_SECS=3 (whole run ~90s vs ~382s) and POLL the establish ping until discovery converges within the cert window (fixes the single-ping race). - integration.yml: run #41 POLL-ONLY, matching its fork source run-netns- discovery.sh — gossip discovery convergence is flaky under io_uring and #41's cert-revocation logic is driver-agnostic. #36 stays both-driver. Verified locally: #41 poll passes ~85s; #36 both drivers still pass.
This was referenced Jul 23, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #36 and #41. Two independent session-lifecycle fixes in
bin/yipd, both unblocked by the merged 9a/#91 rekey machinery. No wire change;yip-crypto/yip-wire/handshake.rsuntouched; both are no-ops with membership off / no handshake in flight (2a/2b/2c byte-identical).#36 — path-switch re-initiation half-opens a session
A path switch (Punch→Relay, Punch C1→C2) drew a fresh Noise ephemeral, so a responder already Established over the old path only replayed a
cached_respkeyed to the original ephemeral → the initiator never completed → silent bidirectional black hole with the remote falsely "up".retarget_handshakepreserves the in-flight ephemeral across a re-target (resend the existinginit_pktinstead ofbegin_handshakeminting fresh).started_msis not reset (the 90 s give-up still bounds ephemeral reuse);last_sent_msis reset (no redundant back-to-back Init); the relay branch clearsendpoint, the direct branch re-stamps it (sohandle_handshake_respmatches the reply). The two tick escalation arms call it.init_eph == cached_resp_init_eph— proven the original peer, harmless replay) adopts the relay for its egress and replayscached_resp, closing the headline scenario. A relayed Init with a new ephemeral against a direct peer stays 9a follow-up: relay-path session rekey (relay-only sessions get no forward-secrecy rotation) #91 fail-closed.relay_wrapaddresses the peer's registered node;current's keys unchanged) and degrades direct rekey. Documented in the spec.#41 — cert revocation lag exceeds cert lifetime
A revoked (cert-expired, non-renewed) mesh member kept its
Establishedsession until process restart, because rekey and cold-start re-admission both re-used the session identity without re-checking the cert.drop_session— tear down a peer's session (remove itscurrent/next/previousconn_tags fromby_tag, revert toIdle).drop_session+ the sweep only flap, not revoke).is_roothelper exempts always-admit roots from all cert gates (roots are trusted via the signed root set, not a member cert). No-op when membership isNone.Verification
cargo test -p yip-ca(4),clippy -D warnings,cargo fmt— all clean.run-netns-pathswitch-rehandshake.sh(2b: path-switch re-initiation can half-open a session (needs rekey/anti-replay) #36): A escalates punch→relay while B is direct-established → A converges over the relay (20/20 ping), DISTINCT_INIT_EPHEMERALS=1 across 6 Inits (the ephemeral is preserved — no cold-start churn), relay-forwarded>0.run-netns-cert-revocation.sh(2c: cert revocation lag exceeds cert lifetime without session rekey (#9) #41): A's short-lived cert expires → B drops A's session within a bounded window (100% loss) and A cannot re-establish (the re-admission gate refuses the expired cert). (yip-ca sign-certgains--secs Nfor minting the short-lived cert.)Known follow-up
For symmetry with the new gates, the pre-existing initiator-completion cert checks (
handle_handshake_resp/relayed_handshake_resp) could also gain theis_rootexemption — latent only (roots present valid certs in practice), out of this branch's delta. Filed separately.