Hi folks,
I would like to ask for clarification regarding Local Network Policy. I found 2 cases where I think the behaviour differs from the documentation.
1. Use case
In a CI environment, we have multiple services (LaunchAgents) which require Local Network Access. We are fine by manually approving the Local Network Permission once (per service), but we also require these services to be able to self-update. Self update results in downloading the a binary with an (obviously) different UUID, which seems to result in re-triggering the Local Network Consent prompt. Strange thing: If I don't click either buttons (Allow of Reject), just restart macOS, it will result in an enabled entry in Privacy & Security > Local Network. I read a reply somewhere on this forum by an Apple engineer that the approval process is a mix of Bundle ID + UUID + other components, so I would expect a new binary with the same properties (but different UUID) to be already whitelisted. Is this behaviour intended?
2. Use Case
Given the first issue, I decided to do this in the "right way". I was happy to read this sentence in the documentation:
If you ship a launchd agent that’s not installed using SMAppService, make macOS aware of the responsible code by setting the AssociatedBundleIdentifiers property in your launchd property list.
I have a properly setup (and code signed) Application, for which I have enabled Local Network permission in Privacy & Security.
I have a standalone LaunchAgent, which runs a long running binary from a user directory. The agent is managed with launchd, and in this sense it is "independent" from the main Application Bundle. I have setup AssociatedBundleIdentifiers in the Agent plist, to associate it with the Application. The TeamIdentifier of the 2 binaries are the same. Based on the documentation, this should be enough for my agent to signal macOS that the responsible code is the Application Bundle (which is already enabled from Local Network POV).
Instead, once the LaunchAgent starts, the Local Network Consent popup appears for the binary. I would expect the Application to be the responsible code, thus no more Consent popup.
Is this behaviour intended?
I need this service to run as user
, so I cannot just circumvent the Consent popup by running as a Daemon or Root. Nor I would like to manage the Agent with ServiceManagement. What do you guys think, have I misunderstood the documentation?
The canonical solution to this problem is to sign your code with a stable code-signing identity, such that version N+1 satisfies the same designated requirement as version N. See TN3127 Inside Code Signing: Requirements. Are you not doing that?
In general, the AssociatedBundleIdentifiers
property should be sufficient for the system to track responsibility from your launchd
agent to its associated app.
I have a test project that I use to exercise cases like this, and I just tried it [1] and the results were interesting. My test project is able to perform various privileged operations in various execution contexts. So, I ran two tests in a launchd
agent:
-
Starting a
CGEventTap
, which requires the Input Monitoring privileged -
Triggering the local network privacy alert, using the code from TN3179
In the first test the system was able to correctly identify the responsible code via AssociatedBundleIdentifiers
. In the second test it was not. Instead of showing the app name, it showed the agent name. That seems to match your experience.
I’ve encountered numerous problems like this with local network privacy, and I’ve filed my own selection of bugs about them. If you’re still seeing this problem on the latest macOS release, I recommend that you file your own bug about it.
Please post your bug number, just for the record.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] On macOS 15.2. I really need to update!