Skip to content

test(m8a): harden cutover artifacts for M9 (guard table_down, apply/rollback trap, idempotent handover drop)#164

Merged
ryanmurf merged 1 commit into
masterfrom
feat/m8a-artifact-safety
Jul 17, 2026
Merged

test(m8a): harden cutover artifacts for M9 (guard table_down, apply/rollback trap, idempotent handover drop)#164
ryanmurf merged 1 commit into
masterfrom
feat/m8a-artifact-safety

Conversation

@ryanmurf

Copy link
Copy Markdown
Owner

M8a cutover-artifact hardening (task #40, review Part B)

Fixes the 3 MAJOR + 1 MINOR artifact-safety findings from the independent
M8a review before M9 invokes these scripts LIVE on tron's host. Scope is
confined to tests/m8a-cutover/ (shell + docs + new isolated tests); no Rust
touched, so the Rust CI is unaffected.

Fixes

(a) MAJOR — cutover_table_down could delete the live firewall. The README
M9 example set NFT_TABLE=voicefw; cutover_table_down did an unconditional
nft delete table inet voicefw, which wipes the entire live firewall (trusted
allowlist, SIP 45070 drop, RTP 20000-20100 drop, v6 drops). Now:

  • cutover_table_down deletes only a table this tool created — two gates
    must both pass: (1) a protected-name denylist (voicefw filter nat mangle raw security), and (2) an ownership sentinel chain (m8a_cutover_owner,
    a never-hooked regular chain that cutover_table_up installs). Either gate
    failing => it refuses (returns 3) instead of deleting.
  • README M9 example rewritten to use the default, SEPARATE cutover table
    (coexists with voicefw at input-hook priority -10, both policy accept);
    NFT_TABLE=voicefw removed.

(b) MAJOR — no error trap => stuck handover drop. apply-fs-to-rustisk.sh
and rollback-rustisk-to-fs.sh (set -euo pipefail, no trap) left the blanket
udp dport <PORT> drop ON if any mid-sequence step failed (phone outage until
manually cleared). Added an EXIT trap that idempotently runs
handover_drop_off on every exit path and preserves the original exit code.

(c) MAJOR/MINOR — non-idempotent drop rules. handover_drop_on stacked a
duplicate on a second call; handover_drop_off used head -n1 (removed one).
Now drop_on is idempotent (second call = no-op) and drop_off removes ALL
handover rules.

(d) MINOR — window band. RESULTS.md rollback mechanism-floor window
widened ~49–52 ms~38–52 ms to match the reproduced band.

Red-capable tests (container-isolated)

New safety-tests.sh + run-safety-tests.sh, run inside ONE isolated container
netns (--privileged --network none, only lo, host LAN NO ROUTE, own empty
nft ruleset, synthetic ports 55071/55072 — never 45070 / RTP 20000-20100 /
the host firewall / live stack). Each test reproduces the pre-fix bug (RED) and
proves the fix (GREEN). Actual run transcript:

--- ISOLATION (must all hold before any nft op) ---
  main-netns links: lo,
  host LAN 192.168.0.109: NO ROUTE (isolated) OK
  nft ruleset before tests: EMPTY OK
  synthetic ports only: 55071 / 55072 (NEVER 45070 / RTP 20000-20100)

=== T1: cutover_table_down refuses to delete a table it did NOT create ===
  RED (pre-fix): unconditional 'nft delete table inet voicefw' WIPED the foreign firewall (the footgun)
  PASS T1a guarded cutover_table_down REFUSED foreign 'voicefw' (rc=3); table SURVIVED
       REFUSING: 'inet voicefw' is a protected host table; cutover_table_down will not delete it.
  PASS T1b REFUSED a non-denylisted foreign table lacking the owner marker (rc=3); survived
       REFUSING: table 'inet foreign_tbl' exists but lacks the m8a-cutover ownership marker (chain m8a_cutover_owner); not ours to delete.
  PASS T1c cutover_table_down DELETED a table it created (owner marker present, rc=0)

=== T2: mid-sequence apply failure leaves NO lingering handover drop (EXIT trap) ===
  RED (pre-fix): mid-sequence failure with NO trap left 1 handover drop(s) STUCK ON (outage)
  PASS T2 apply FAILED mid-sequence (rc=1) yet EXIT trap cleared ALL handover drops (0 left)

=== T3: double drop_on + one drop_off leaves ZERO handover rules ===
  RED (pre-fix): 2x old_drop_on => 2 rules; one old_drop_off => 1 left (blanket drop STUCK)
  PASS T3 idempotent: 2x drop_on => 1 rule; one drop_off => 0 left

PASS=5 FAIL=0
M8a SAFETY TESTS: PASS

Regression: original proof still green

Re-ran run-proof.sh with the guarded/idempotent/trap changes — the listener-swap
proof still passes end-to-end (M8a RESULT: PASS, RED teeth confirmed, detector
self-test all OK, source-drops 0/0, clean FS→RUSTISK→FS boundaries). All testing
was container-isolated; the host nftables, live voicefw firewall, port 45070,
and RTP 20000-20100 were never touched.

…ollback trap, idempotent handover drop)

Fixes the 3 MAJOR + 1 MINOR artifact-safety findings from the independent
M8a review (Part B) before M9 runs these on tron's host:

(a) cutover_table_down is GUARDED — it now deletes only a table THIS tool
    created: a protected-name denylist (voicefw + system tables) AND an
    ownership sentinel chain that cutover_table_up installs. A stray
    NFT_TABLE=voicefw can no longer wipe the live firewall (trusted-source
    allowlist, SIP 45070 drop, RTP 20000-20100 drop). README M9 example now
    uses the default SEPARATE cutover table (coexists with voicefw at
    priority -10) instead of NFT_TABLE=voicefw.

(b) apply-fs-to-rustisk.sh + rollback-rustisk-to-fs.sh add an EXIT trap that
    idempotently runs handover_drop_off on ANY failure path, so a
    mid-sequence abort under set -euo pipefail never leaves the blanket
    'udp dport <PORT> drop' stuck ON (phone outage).

(c) handover_drop_on is idempotent (a second call is a no-op, no duplicate
    rule); handover_drop_off removes ALL handover rules (was head -n1 = one).

(d) RESULTS.md rollback mechanism-floor window widened to ~38-52 ms to match
    the reproduced band.

Adds red-capable regression tests (safety-tests.sh + run-safety-tests.sh)
run in an ISOLATED container netns (--privileged --network none, synthetic
ports 55071/55072, never 45070/RTP/host): each proves the pre-fix bug (RED)
and the fix (GREEN) — guard refuses foreign voicefw + owner-less table,
mid-sequence failure leaves 0 lingering drops, double-on+one-off leaves 0.
@ryanmurf
ryanmurf merged commit 3722499 into master Jul 17, 2026
3 checks passed
@ryanmurf
ryanmurf deleted the feat/m8a-artifact-safety branch July 17, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant