NEFilterManager loadFromPreferences Error on macOS

Hello I'm getting an error when I try to do the following:
Code Block swift
NEFilterManager.shared().loadFromPreferences { loadError in
DispatchQueue.main.async {
var success = true
if let error = loadError {
os_log("Failed to load the filter configuration: %@", error.localizedDescription)
success = false
}
completionHandler(success)
}
}


The specific error I get is:
Code Block text
Failed to load configurations: Error Domain=NEConfigurationErrorDomain Code=11 "IPC failed" UserInfo={NSLocalizedDescription=IPC failed}


The code I'm testing is almost identical to Apple's SimpleFirewall example. The main difference is that I'm trying to kick things off immediately in the AppDelegate from applicationDidFinishLaunching(). I've gotten the SimpleFirewall example working so I cannot for the life of me figure out what's wrong here.

Answered by Systems Engineer in 631147022

So we tried those specific bundle ids, group ids, and NEMachServiceName. When we used
those it worked successfully. Thanks for that!

No problem. Glad that is up and running for you.

Maybe the network extension needs to have the container app as an effective prefix (or at least this is a best practice).

Yes, there are a few things that stick out to me here. First make sure your Network System Extension using the existing prefix from the container app.

Contain App Bundle ID:
com.example.foo.bar.dev

Network Extension Bundle ID:
com.example.foo.bar.dev.networkextension

Your Network Extension Bundle ID is a new bundle id entirely.
com.example.foo.bar.dev3

Next, building upon this pattern, use the following setup:

Contain App Bundle ID:
com.example.foo.bar.dev

Container App Group:
com.example.foo.bar.dev

Network Extension Bundle ID:
com.example.foo.bar.dev.networkextension

Network Extension App Group:
$(TeamIdentifierPrefix)com.example.foo.bar.dev

Network Extension Info.plist NEMachServiceName:
$(TeamIdentifierPrefix)com.example.foo.bar.dev.networkextension

Keep the sandbox enabled on both the container app and Network System Extension.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
In which step did you receive this error from applicationDidFinishLaunching when you reproduced this? The first or the third?

1) NEFilterManager.shared().loadFromPreferences
2) OSSystemExtensionRequest.activationRequest
3) NEFilterManager.shared().loadFromPreferences
4) NEFilterManager.shared().saveToPreferences

Either way it looks like the XPC connection out to the IPCConnection class is failing for some reason and this would be one part you could take a look at as well.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
It was the first. I found out that one of the entitlement files wasn't updating correctly when I made changes to the Project's Signing and Capabilities. I added the value manually to the entitlement file directly and relaunched Xcode. I got a bit father but now the error is

Code Block text
Failed to register with the XPC network extensions: Couldn’t communicate with a helper application.

Again this is seemingly happening at the first step before it actually replaces the network extension with the activation request.
Do you have the bundle identifier for the Network System Extension correctly set for the NEFilterManager to recognize it? If so, try setting this up out and the NSViewController to see if you get the same issue.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
I verified that the bundle identifiers are correct at run time. I commented out everything in my AppDelegate and added a ViewController.swift that matches the Simple Firewall. Without me clicking on one of the buttons I immediately get the error:
Code Block text
Failed to register with the XPC network extensions: Couldn’t communicate with a helper application.

This is happening in the Network Extension when the app calls to register itself at the line:
Code Block swift
newConnection.remoteObjectProxyWithErrorHandler()

I checked the newConnection variable between the two programs. Nothing looked out of the ordinary other than Apple's SimpleFirewall project has an internal _state of 4 where as my project's internal _state is 0.
Okay it looks like you are hitting a snag setting up either the ProviderCommunication or AppCommunication protocol between your provider and container app. To debug this, pay attention to what is being done here in IPCConnection :: register.

Code Block swift
let machServiceName = extensionMachServiceName(from: bundle)
os_log(.debug, log: self.log, "MachServiceName: %{public}@", machServiceName)
let newConnection = NSXPCConnection(machServiceName: machServiceName, options: [])
/* The exported object is the delegate. */
newConnection.exportedInterface = NSXPCInterface(with: AppCommunication.self)
newConnection.exportedObject = delegate
/* The remote object is the provider's IPCConnection instance. */
newConnection.remoteObjectInterface = NSXPCInterface(with: ProviderCommunication.self)


Put a log statement in and make sure you are pulling the correct MachServiceName from the Providers info.plist.

Code Block xml
<key>NetworkExtension</key>
<dict>
<key>NEMachServiceName</key>
<string>$(TeamIdentifierPrefix)com.example.apple-samplecode.containerapp.myextension</string>
<key>NEProviderClasses</key>
<dict>
<key>com.apple.networkextension.filter-data</key>
<string>$(PRODUCT_MODULE_NAME).FilterDataProvider</string>
</dict>
</dict>


Next, make sure that both the ProviderCommunication and AppCommunication protocols are setup in IPCConnection.swift and ViewController.swift (or AppDelegate.swift if this is the way you go).

Code Block swift
/* App --> Provider IPC (IPCConnection.swift) */
@objc protocol ProviderCommunication {
func register(_ completionHandler: @escaping (Bool) -> Void)
}
/* Provider --> App IPC (ViewContoller.swift or AppDelegate.swift) */
@objc protocol AppCommunication {
func promptUser(aboutFlow flowInfo: [String: String], responseHandler: @escaping (Bool) -> Void)
}


If you run into problems, os_log is there for you. Log each step of the way using the technique I demonstrated above with MachServiceName. Then checkout the results using % log stream --level debug --predicate 'subsystem == "com.example.apple-samplecode.containerapp.myextension"'


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
It seems like I'm getting the right value from the NEMachServiceName. I noticed that if I use the NEMachServiceName from the Simple Firewall program,
Code Block text
$(TeamIdentifierPrefix)com.example.apple-samplecode.SimpleFirewall.SimpleFirewallExtension
and the app group to
Code Block text
$(TeamIdentifierPrefix)com.example.apple-samplecode.SimpleFirewall
everything works. The bundle ids remain something else entirely. It feels like it's reusing the connection from the SimpleFirewall though.

Is there some guideline what the values for the AppGroup and NEMachServiceName need to be? I know both need to be prefixed with $(TeamIdentifierPrefix) but beyond that I thought it could be anything. If I do something like $(TeamIdentifierPrefix)bundleid.containerapp.myextension it doesn't seem to work (where containerapp and myextension are the Target names). Also I renamed my Network Extension's Target Name, I can't find any instance of the old name but could that potentially cause issues?
I think we figured out the issue. By removing the App Sandbox capability from both the container app and the network extension, everything is working. The SimpleFirewall example has the App Sandbox capability yet still works, why is that?

I think we figured out the issue. By removing the App Sandbox capability from both the container app and the network extension, everything is working. The SimpleFirewall example has the App Sandbox capability yet still works, why is that?

The SimpleFirewall example has a sandbox on both the container app and Network System Extension because this is a requirement for working with Network Extensions. This may have resolved your issue but this is not a viable solution for distribution on the App Store or through Developer ID.

Removing the sandbox does actually elude to what it sounds like you're running into. You mentioned:


I noticed that if I use the NEMachServiceName from the Simple Firewall program,
$(TeamIdentifierPrefix)com.example.apple-samplecode.SimpleFirewall.SimpleFirewallExtension
and the app group to
$(TeamIdentifierPrefix)com.example.apple-samplecode.SimpleFirewall
everything works.


Notice that the one is a prefixed with the other. Try this out and let me know if this works for you:

Use this as your container app bundle id:
com.example.SimpleFirewallTestBed

Use this as your container app App Groups identifier:
$(TeamIdentifierPrefix)com.example.SimpleFirewallTestBed

Use this as your Network System Extension bundle id:
com.example.SimpleFirewallTestBed.SimpleFirewallExtension

Use this as your Network System Extension App Groups identifier (Which will create a new group):
$(TeamIdentifierPrefix)com.example.SimpleFirewallTestBed

Use this as your Network System Extension NEMachServiceName
<key>NEMachServiceName</key>
<string>$(TeamIdentifierPrefix)com.example.SimpleFirewallTestBed.SimpleFirewallExtension</string>

Keep the sandbox enabled on both the container app and Network System Extension. This should align things as needed and avoid using a previous connection.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
So we tried those specific bundle ids, group ids, and NEMachServiceName. When we used those it worked successfully. Thanks for that! When we try to use the existing ones we created with matching permissions it didn't work. Is there an obvious reason these won't work that I'm missing?

Contain App Bundle ID:
com.example.foo.bar.dev

Container App Group:
$(TeamIdentifierPrefix)com.example.foo.bar.dev

Network Extension Bundle ID:
com.example.foo.bar.dev3

Network Extension App Group:
$(TeamIdentifierPrefix)com.example.foo.bar.dev

Network Extension Info.plist NEMachServiceName:
com.example.foo.bar.dev3

I suspect there might be a few reasons this isn't working. Maybe we cannot use numbers or at least end with one. Maybe there's too many reverse URL segments. Maybe the network extension needs to have the container app as an effective prefix (or at least this is a best practice).
Accepted Answer

So we tried those specific bundle ids, group ids, and NEMachServiceName. When we used
those it worked successfully. Thanks for that!

No problem. Glad that is up and running for you.

Maybe the network extension needs to have the container app as an effective prefix (or at least this is a best practice).

Yes, there are a few things that stick out to me here. First make sure your Network System Extension using the existing prefix from the container app.

Contain App Bundle ID:
com.example.foo.bar.dev

Network Extension Bundle ID:
com.example.foo.bar.dev.networkextension

Your Network Extension Bundle ID is a new bundle id entirely.
com.example.foo.bar.dev3

Next, building upon this pattern, use the following setup:

Contain App Bundle ID:
com.example.foo.bar.dev

Container App Group:
com.example.foo.bar.dev

Network Extension Bundle ID:
com.example.foo.bar.dev.networkextension

Network Extension App Group:
$(TeamIdentifierPrefix)com.example.foo.bar.dev

Network Extension Info.plist NEMachServiceName:
$(TeamIdentifierPrefix)com.example.foo.bar.dev.networkextension

Keep the sandbox enabled on both the container app and Network System Extension.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
NEFilterManager loadFromPreferences Error on macOS
 
 
Q