Hello,
My team has developed a DNS proxy for macOS. We have this set up with a system extension that interacts with the OS, and an always-running daemon that does all the heavy lifting. Communication between the two is DNS request and response packet traffic.
With this architecture what are best practices for how the system extension communicates with a daemon?
We tried making the daemon a socket server, but the system extension could not connect to it.
We tried using XPC but it did not work and we could not understand the errors that were returned.
So what is the best way to do this sort of thing?
Are they just not usable for extensions and daemons to communicate?
Both Unix domain sockets and XPC work fine for this. However, an NE sysex must be sandboxed, so you have to use these constructs in a way that’s compatible with the App Sandbox.
Lemme break down all the cases.
Outgoing XPC
There are two options for this:
-
In the daemon, have the listener name the XPC endpoint such that it ‘lives’ within an app group, then include that app group in your NE sysex’s entitlements. The sandbox allows such outgoing connections, as documented in App Groups Entitlement (search for “Mach IPC”).
-
Explicitly allow the connection using the
com.apple.security.temporary-exception.mach-lookup.global-name
temporary exception entitlement. I have a link to the docs in App Sandbox Resources.
Everything related to app groups is complicated on the Mac. See App Groups: macOS vs iOS: Fight! That’s one of the reason why I suggested glomming things together.
Also, temporary exception entitlements cause App Review entanglements, which is why I asked about your distribution channel.
Note Don’t worry about the temporary in the name. They’re only temporary from the perspective of the Mac App Store. If you’re distributing directly, it’s fine to rely on such entitlements like you would any other API.
Incoming XPC
Your NE sysex can listener on a named XPC endpoint using NEMachServiceName
. Your daemon is likely not sandboxed, so it can connect to it without any challenges.
Outgoing Unix Domain Socket
To set this up, have your daemon put the socket in an app group container and then include that app group in your NE sysex’s entitlements. The sandbox allows such outgoing connections, as documented in App Groups Entitlement (search for “UNIX domain sockets”).
Incoming Unix Domain Socket
This is just the reverse. Have the NE sysex put the listener socket in an app group container and then connect to that from the daemon.
Note Traditionally the daemon, not being sandboxed, didn’t even need the app group entitlement. Starting with macOS 15 it will, due to app group container protection. As I mentioned above, everything related to app groups is complicated on the Mac |-:
There’s one huge caveat here: Your listener (either XPC or Unix domain socket) must authorise incoming connections in some way. Folks are often confused by this. They look at all the security palava and think that the OS will protect them from ‘rogue’ connections. It does in some cases, but that’s not something you want to bet the farm on.
For XPC, a common approach is the one outlined in Validating Signature Of XPC Process.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"