Repository navigation
iroh: authenticate the client to a private JWT-gated relay (bind-path token) - #7826
lawrencecchen wants to merge 2 commits into
Conversation
…path The client can now authenticate to a private, JWT-gated iroh relay. When a custom relay_url AND a non-empty relay_auth_token are supplied to the bind FFI, the relay map is built as RelayMap::from(url).with_auth_token(token); otherwise behavior is unchanged (token ignored on the default n0 map, disabled relays, or when absent). Scope: bind path only. The token is never placed in route JSON, pairing codes, or logs, so it cannot leak through the dial/route-serialization paths. All Swift call sites pass nil for now (default no-token behavior); minting the JWT via the web API is a follow-up (TODOs left at the composition roots). Verified: cargo build/test/clippy pass (new test asserts the token lands only on a custom map); swift build passes for CMUXMobileCore, CmuxIrohFFI, CmuxMobileIrohTransport.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds bind-time auth for private iroh relays. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (2): Last reviewed commit: "cmux-iroh: bump iroh 1.0.0-rc.1 -> 1.0.2..." | Re-trigger Greptile |
| } else if let Some(url) = custom_relay { | ||
| let relay_map = RelayMap::from(url); | ||
| let relay_map = if let Some(token) = relay_auth_token { | ||
| relay_map.with_auth_token(token) |
There was a problem hiding this comment.
When Swift passes relayAuthToken through withCString, that buffer is only valid for the FFI call. If with_auth_token stores the &str instead of cloning it into the relay map, the endpoint can later read freed memory while authenticating to the relay, causing corrupt auth headers or a crash. Make the ownership boundary explicit before storing the token in the long-lived relay configuration.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a81299c63c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let relayAuth = relayAuthToken | ||
| // Preserve the existing default-map bind when no token is available; | ||
| // a non-empty token makes the peer relay the local authenticated map. | ||
| let bindRelay = relayAuth?.isEmpty == false ? relay : nil |
There was a problem hiding this comment.
Verify relay before sending auth tokens
When relayAuthToken is non-empty, this treats whatever relay_url came from the route as the authenticated relay. That URL is not a trusted token audience: cmux_iroh_endpoint_route_json serializes iroh's current home relay for both default/public relays and user-supplied routes, and CmxIrohByteTransportFactory holds one token for every .iroh route. Once the follow-up starts passing a real cmux JWT, a stale or malicious route can make the client present that token to an arbitrary relay, and default routes are also converted into a single custom relay map instead of using the intended default map; only bind with the token after matching the route relay to the token's expected relay/audience.
Useful? React with 👍 / 👎.
Matches the #7327 crate bump + the 1.0.2 relay fleet. The relay_auth_token bind path (RelayMap::from(url).with_auth_token) compiles unchanged on 1.0.2.
Threads an optional relay auth token (a cmux-signed EdDSA JWT) through the cmux-iroh bind path so the client can use a private, JWT-gated iroh relay instead of being denied.
What
relay_auth_token: *const c_charparam oncmux_iroh_endpoint_bindandcmux_iroh_endpoint_bind_relay_only(andbind_impl). When a customrelay_urland a non-empty token are supplied, the relay map is built asRelayMap::from(url).with_auth_token(token); otherwise behavior is unchanged (token ignored on the default n0 map, on disabled relays, or when absent).relay_mode()helper shared by both bind entrypoints.CmxIrohByteTransport(+FFI),CmxIrohByteListener, factory) thread an optionalrelayAuthToken: String? = nil.Security
Bind-path only. The token is never placed in route JSON, pairing codes, or logs, so it cannot leak through the dial/route-serialization paths (which is why
MobileRouteResolver/CmxTransportare deliberately untouched). All call sites passniltoday; minting the JWT via the web API is a follow-up (TODOs at the composition roots), so this PR is behavior-neutral.Why now
A private relay fleet (separate repo) is live and requires a valid cmux JWT; anonymous is denied. Proven end-to-end against the deployed relay: a valid token relays real traffic, missing/bad tokens are denied (relay logs: allow for the valid endpoints, deny for the rest). This PR is the client half so cmux can present that token.
Tests
bind_relay_mode_applies_auth_token_only_to_custom_mapasserts the token lands only on a custom map (present with URL+token; absent for null/empty; token-without-URL falls back toRelayMode::Default).cargo build/test/clippy -D warningspass.swift buildpasses forCMUXMobileCore,CmuxIrohFFI,CmuxMobileIrohTransport.MobileIrohHostLane,cmuxApp) passnilwith matching labels; CI compiles them here (not locally checkable without Xcode).Based on
feat-ios-device-list-v2(#7327), which already addedRelayMode::Custom.Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Medium Risk
Touches relay authentication and a breaking FFI bind signature, but tokens are bind-only, excluded from routes, and all entry points still pass nil until JWT minting ships; the iroh 1.0.2 bump adds dependency regression surface.
Overview
Adds optional relay authentication at endpoint bind so clients can home on a custom, JWT-gated private relay, and bumps the pinned
irohcrate from1.0.0-rc.1to1.0.2(with matchingCargo.lockchurn).The C FFI gains a
relay_auth_tokenargument oncmux_iroh_endpoint_bindandcmux_iroh_endpoint_bind_relay_only. Relay selection is centralized in arelay_mode()helper: with a customrelay_urland a non-empty token, the map is built asRelayMap::from(url).with_auth_token(token); otherwise behavior is unchanged (token ignored on default n0 map, disabled relays, or missing URL). The token is documented as never logged or included in route JSON; dial/connect APIs are unchanged.Swift threads
relayAuthTokenthrough the listener, dial transport, factory, and a sharedbindEndpointhelper. On dial, a non-empty token switches the local bind from the default fleet to the peer’s relay URL for authenticated homing, while relay URL hints on connect stay as before. Mac host and iOS app still passnil(TODO: mint via web API), so production behavior stays neutral until token wiring lands.A Rust unit test
bind_relay_mode_applies_auth_token_only_to_custom_maplocks in token placement rules.Reviewed by Cursor Bugbot for commit 266adb1. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds optional bind-time JWT auth for custom
iroh-relayso clients can use a private, token-gated relay. Also bumpsirohto1.0.2. Behavior is unchanged unless both a customrelay_urland a non-empty token are provided.New Features
relay_auth_tokentocmux_iroh_endpoint_bindandcmux_iroh_endpoint_bind_relay_only; applied only with a customrelay_urland relays enabled, otherwise ignored.relay_mode()to build the relay map and attach the token only to a customRelayMap.CmxIrohByteTransport(+FFI),CmxIrohByteListener, andCmxIrohByteTransportFactoryacceptrelayAuthTokenand pass it at bind; token is never in dial hints, route JSON, pairing codes, or logs.Dependencies
irohfrom1.0.0-rc.1to1.0.2; updated lockfile to match the relay fleet.Written for commit 266adb1. Summary will update on new commits.