Repository navigation
Verify mimic listeners with one lsof pass and give port-forward RPCs time - #94
Merged
Merged
Conversation
…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
approved these changes
Oct 28, 2026
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.
chrissyrocket
approved these changes
Oct 28, 2026
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.
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: onelsof -n -P -l -a -iTCP:<p1> -iTCP:<p2> ... -sTCP:LISTEN -Fpunper phase instead of one per listener. Records are grouped by port and each listener is judged exactly as before (present, sensor uid, exactip:port, one pid). lsof exit 1 (no match) is handled as "listener missing", judged per endpoint, not as a tool failure.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.0forsetupPortForwardsandclearPortForwardsonly. 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_mimicoff the helper during scans, retrying the PF restore, and quieting the duplicate-address traceback. Tracked in the issue. Control surface only.