test(sip): M6 CP5 (B4) — dynamic authenticated REGISTER + IP-change routing (receiver-side, in-process) - #154
Merged
Conversation
Owner
Author
Review noteThis is a test-only PR — it adds Not included: CP5's isolated-Docker container-restart harness (bridge container restarted with a new pod IP) — flagged as the remaining CP5 acceptance step for continuation. |
…eceiver-side (M6 CP5, B4) The rustisk registrar-side B4 mechanism already exists (resolve_endpoint_contact prefers the live registration over a static contact, issue #33; Registrar:: best_contact returns the most-recently-registered non-expired contact). This adds the missing committed RECEIVER-SIDE regression test for the pod-restart- new-IP property, with real UDP sockets: 1. the bridge digest-REGISTERs from address A (401 -> digest -> 200); 2. an outbound Dial routes to A — the INVITE datagram lands on A's socket; 3. the bridge 'restarts': a new socket B re-REGISTERs (digest) from a new addr; 4. a subsequent Dial FOLLOWS to B — the INVITE lands on B, NOT the stale A. RED (captured in the PR): make best_contact oldest-wins -> after the re-REGISTER the INVITE datagram fails to reach B ("must FOLLOW to the re-REGISTERed address B") -> receiver-side RED. Scope: this is the in-process rustisk-side mechanism proof. CP5's isolated-Docker container-restart harness (bridge container restarted with a new pod IP) is a separate acceptance run out-of-band and is NOT included here.
ryanmurf
force-pushed
the
feat/m6-cp5-dynamic-authenticated-register
branch
from
July 17, 2026 10:35
18d69e5 to
0ec8912
Compare
ryanmurf
added a commit
that referenced
this pull request
Jul 17, 2026
…gression (M6 CP5, B4) (#155) * test(sip): isolated-Docker container-restart REGISTER acceptance harness (M6 CP5, B4) On-demand harness (tests/cp5-register-restart/) proving rustisk's dynamic authenticated REGISTER end-to-end with a REAL bridge container restart on an isolated --internal Docker network — the literal CP5/B4 acceptance that PR #154 landed only as an in-process mechanism proof. A sip-bridge container digest-REGISTERs (401->digest->200) advertising its Docker-assigned IP A; Dial(PJSIP/bridge) lands an INVITE at A (captured receiver-side on the bridge). The bridge container is destroyed, a sentinel seizes the vacated A, and a fresh bridge comes up with a new IP B (B!=A, enforced) and re-REGISTERs. A subsequent Dial FOLLOWS to B receiver-side and NOT to stale A (sentinel silent). Both stale-A and fresh-B bindings stay live, so best_contact's newest-wins is genuinely load-bearing. A/B are read at runtime, never hardcoded. RED negative control (captured): routing is defeated by a static contact pinned to A (bridge_pinned); the Dial misroutes to stale A, the sentinel catches it, and the follow-to-B assertion goes RED — proving the A-detection is real. Reaps every container/network on exit (host-PID kill for tron's userns-remapped docker); generates a throwaway test PIN, shredded on exit; never touches the live stack. Not wired into CI (on-demand). * test(sip): harden CP5 harness against false-green (codex review) - sip_agent.py: a 200 to the UNAUTHENTICATED REGISTER is now a hard failure (exit 3), not success — a regression that bound an unauthenticated contact would otherwise green-light the digest-auth proof. - run.sh: validate CP5_CASE up front (all|green|red) so a typo cannot skip the case-gated assertions and still exit 0. - pjsip.conf/run.sh: give bridge_pinned a distinct contact user (sip:pinned@A) and correlate the RED capture on that Request-URI, so no stray 'bridge' datagram can false-pass RED. - run.sh: harden reap_container (retry host-PID acquisition, timeout-wrap every docker op) and raise a loud CLEANUP WARNING on any leaked container/network. - ami_originate.py: require the Originate-specific 'successfully queued'; a bare 'Success' (also emitted by Login) no longer counts as Originate success. - README: correct 'shred' wording and document the distinct pinned user. --------- Co-authored-by: Repin Agent <repin-agent@herodevs.dev>
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.
M6 CP5 (B4) — dynamic authenticated REGISTER + IP-change routing: rustisk-side receiver-side proof
Context. The sip-bridge advertises an EPHEMERAL pod IP and REGISTERs; a static contact is wrong
because a pod restart gives it a NEW IP (B4). The rustisk registrar-side mechanism for this ALREADY
EXISTS:
SipChannelDriver::resolve_endpoint_contactprefers the live registrar binding over the staticconfigured contact (issue REGISTER: any configured user's credentials can bind any AoR; registrar bindings are never used for routing #33);
Registrar::best_contactreturns the most-recently-registered non-expired contact.So a re-REGISTER from a new address updates the routing. What was missing is a committed
receiver-side regression test of the pod-restart-new-IP property. This PR adds it.
RECEIVER-SIDE PROOF (
e2e_dynamic_register.rs, real UDP sockets).The REGISTER is a genuine digest round-trip (401 challenge →
create_digest_response→ 200), so itproves authenticated dynamic registration; the outbound routing is exercised through the real
ChannelDriver::request/callpath (direct transport send).CAPTURED RED. Make
Registrar::best_contactoldest-wins instead of newest-wins:(the INVITE datagram fails to reach B — receiver-side RED).
SCOPE / what remains. This is the in-process rustisk-side mechanism proof. CP5's stated
acceptance also calls for an isolated-Docker container-restart harness (a bridge container
restarted so it comes back with a NEW pod IP on an
--internalDocker network, then a call thatroutes to the new IP). That containerized harness is a separate proof, run out-of-band, and is NOT
included in this PR — it is flagged for continuation. The rustisk code proven correct here is
exactly what that harness exercises.
GATES.
cargo test --workspace --exclude pjsip-shimgreen;cargo clippy --workspace --exclude pjsip-shim -- -D warningsclean. Rust 1.97.0. No rustisk source change (test-only;mechanism pre-exists via issue #33).