Skip to content

Verify mimic listeners with one lsof pass and give port-forward RPCs time - #94

Merged
mattmacrocket merged 4 commits into
mainfrom
fix/helper-listener-ownership-check
Oct 28, 2026
Merged

mattmacrocket merged 4 commits into
mainfrom
fix/helper-listener-ownership-check

Conversation

@mattmacrocket

Copy link
Copy Markdown
Collaborator

Partial fix for #93 (the common case). Found during the functional pass on the Mac Studio install.

What the log showed

A port-forward RPC timed out at 30 s during a 41 s ARP phase; the sensor rolled the mimic back; three later requests failed with Mimic backend listener is not an exact sensor-owned VIP bind; then [CRITICAL] Failed to restore packet-filter state. Full timeline in the issue.

Helper (RPCMethods.swift)

  • requireSensorOwnedListeners: one lsof -n -P -l -a -iTCP:<p1> -iTCP:<p2> ... -sTCP:LISTEN -Fpun per phase instead of one per listener. Records are grouped by port and each listener is judged exactly as before (present, sensor uid, exact ip:port, one pid). lsof exit 1 (no match) is handled as "listener missing", judged per endpoint, not as a tool failure.
  • Failure text names the endpoint and the condition: has no listening socket, is owned by uid N, not the sensor, is bound to X, not the exact VIP, is held by N processes, not one. Next time this fires, install.log says what happened.
  • requireSensorOwnedListener (single) delegates to the batched check; existing tests unchanged.

Sensor (privileged/xpc.py)

PORT_FORWARD_RPC_TIMEOUT = 180.0 for setupPortForwards and clearPortForwards only. Every other RPC keeps the 30 s default.

Tests

Red first: four Swift tests (one invocation covers all listeners; missing listener named; the three failure kinds distinct and named; single entry point delegates) failed to compile against the old helper; one sensor test asserted the long timeout on both calls and failed on the missing constant. Green after. swift test: 424 + 125 + 39. test_privileged.py: 48 passed. ruff clean.

Not in this PR

Treating a timed-out RPC as unknown state before rolling back, keeping restart_mimic off the helper during scans, retrying the PF restore, and quieting the duplicate-address traceback. Tracked in the issue. Control surface only.

…time

A fleet-wide PF anchor load named every mimic's backend listeners, and the
helper verified each one with its own full-process lsof, before and after
the load: about 64 process scans per request for ten mimics, served one
request at a time behind whatever ARP scan was in flight. In the field
the sensor's 30 s RPC budget expired, the sensor rolled a healthy mimic
back, and the helper's late lsof reported the closed listener with the
same message as a wrong-owner bind.

The helper now runs one lsof per phase covering every listener port,
groups the records by port, and judges each listener on its own. The
failure names the endpoint and says which condition failed: no listening
socket, foreign owner, wrong bind, or more than one process. The
single-listener entry point delegates to the batched one so existing
callers and tests are unchanged. The sensor gives the two port-forward
RPCs a 180 s budget; every other call keeps 30 s.

Structural follow-ups (timeout as unknown state, scan and restart
serialization, PF restore retry) are tracked in 93.

Swift: 424 + 125 + 39 passed. Sensor privileged tests: 48 passed.
Two package-security tests read the helper source for the owner-check
call by name; the batched check renamed it. Same ordering assertions,
new name.
The old single predicate is now three checks with distinct failure text;
the source test asserts each of them and the missing-socket message.
chrissyrocket
chrissyrocket previously approved these changes Oct 28, 2026
The batched-listener tests read the thrown error from the value of
`#expect(throws:)`. That returns the error on Swift 6.1 and later but
Void on the Swift 6.0 toolchain the app-ci workflow pins (Xcode 16.2),
so the tests saw "()" and failed on every assertion. Capture the
RPCError with do/catch, matching the pattern already used elsewhere in
this file, so the same assertions hold on both toolchains.
@mattmacrocket
mattmacrocket merged commit de8bebb into main Oct 28, 2026
6 checks passed
@mattmacrocket
mattmacrocket deleted the fix/helper-listener-ownership-check branch October 28, 2026 19:55
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.

2 participants