best practices for communication between system extension and daemon

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?

Answered by DTS Engineer in 820365022
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"

The best path forward here depends on a number of factors.

First, is there a reason you need a separate daemon? A system extension is more-or-less equivalent to a launchd daemon. You could radically simplify your life by doing all of this work in the sysex itself.

If that’s not possible then I have a couple of context questions:

  • This is shipping outside the Mac App Store, right?

  • What does the lifecycle of your daemon look like? Do you expect it to start when the Mac boots and then continue running until the Mac shuts down? Or is there something more complex going on?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hi Quinn, it's great to hear from you. This project will not be in the App Store. Our daemon starts at boot time and runs until shutdown.

We've combined our extension and daemon and this may get us down the road. We would prefer to keep them separate for a better architecture.

Local sockets and XPC did not work for us. Are they just not usable for extensions and daemons to communicate?

Our question is: What is the recommended approach for doing 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"

best practices for communication between system extension and daemon
 
 
Q