Skip to content

Workflow status is tracked in GitHub. This local document is retained as an engineering spec/evidence record.

RUST-FEAT-005 - Automated VPN leak-test — assert no data egress off the tunnel (release-blocking)

Summary

Add an automated leak-test that proves emulebb-rust honors the P0 Network Safety invariant: with the VPN tunnel down/unavailable, the client emits zero P2P data-plane traffic (eD2K TCP, Kad/eD2K UDP) off the tunnel interface. The control/REST plane on the local IP is unaffected. This gate is release-blocking.

Why This Matters

The suite's "safe / anonymous" promise rests entirely on fail-closed VPN binding. A leak that ships once destroys the core trust claim. A code-level pin (RUST-FEAT-003) is necessary but not sufficient without an automated test that keeps it true over time.

Intended Shape

  • A test that brings the data-plane up with no/incorrect tunnel and asserts no connect/sendto leaves a non-tunnel interface (e.g. via a deny-by-default firewall/route fixture, or interface-scoped capture asserting zero off-tunnel data packets).
  • Runs in CI as a blocking gate for emulebb-rust; respects the live-wire policy (no public-network contact — the test is local/deterministic).
  • Reuse the same leak assertion shape across networked products where possible so emulebb-rust, qBittorrentBB, and later suite clients share one safety vocabulary.

Acceptance Criteria

  • [x] Tunnel-down scenario: zero eD2K TCP / Kad UDP egress off the tunnel observed.
  • [x] Control/REST plane on the local IP still functions.
  • [x] Test runs blocking in CI and fails if the data plane leaks.

Implementation (2026-07-05)

The dynamic observed-egress gate landed via a test-only egress-audit cargo feature: an audit recorder at the single P2P socket chokepoint (emulebb-kad-net/src/socket_opts/egress_audit.rs, hooked in pin_egress_to_interface) captures every P2P socket's bound local address and the interface index its egress was pinned to. crates/emulebb-core/tests/vpn_leak_egress.rs asserts three scenarios: - tunnel up — every P2P socket is bound to the tunnel IP and pinned to the tunnel interface index (so no datagram/segment can leave a non-tunnel iface); - tunnel down — ZERO P2P sockets open (empty audit) while REST/status answer; - tunnel pulled mid-run — no NEW P2P socket opens (steady-state fail-closed).

Wired blocking into CI via the test-vpn-leak step of tools/rust_quality_gate.py ci-test (cargo test -p emulebb-core --features egress-audit --test vpn_leak_egress -- --test-threads=1; serial because the 3 scenarios share the global recorder). tools/check_rust_client_policy.py guards the feature as test-only (never in a crate's default feature set, never referenced by the daemon binary).

Remaining (additive evidence, NOT release-blocking now that socket-truth gates CI on both OSes): an optional Linux-only wire-truth CI job (dummy tunnel iface + tcpdump asserting zero off-tunnel frames), and the operator Windows wire-truth tools/vpn_leak_local_gate.py (pktmon on the physical NIC with a real hide.me tunnel pull), recorded during the Phase-4 soak.

Notes

  • Pairs with RUST-FEAT-003 (eD2K TCP egress pin). Both are release-blockers per the WORKSPACE-POLICY Network Safety invariant. Cross-product sibling: QBBB-FEAT-004.
  • 2026-06-19 parity closure review: this item is not required to close core MFC parity, but it remains the release-blocking gate for claiming automated fail-closed anonymity/safety. Public live-wire proof is a smoke witness only.
  • 2026-06-17: Added a blocking static Python policy guard for the supported public P2P boundary files. The guard rejects regressions that use optional bind-ifIndex resolution or explicit no-index egress pinning on those paths. This is CI coverage against known leak regressions, but it does not satisfy the dynamic tunnel-down packet-observation acceptance criteria above.
  • 2026-06-17: Extended the static guard to the STUN public-IP/NAT mapping probe path. STUN now requires a resolved bind interface index before DNS/socket activity and passes an explicit ifIndex to egress pinning, so a stale or unassigned P2P bind IP fails closed instead of degrading to optional pinning.