We have a product which uses the NEDNSProxyProvider to provide a custom DNS solution. We're checking for compatibility with macOS Ventura, running Ventura beta 9 in a VM on an 2020 M1 MBA (Monterey host).
We have discovered that system DNS behavior changed with macOS Ventura: If the DNS server specified in Network settings (where all you can set is an IP) supports DoH or DoT, mDNSResponder will automatically use those protocols in preference to traditional port 53 DNS.
We have also discovered that this traffic will completely bypass our active DNSProxyProvider. The Console logs show mDNSResponder communicating directly with the DNS server over a persistent connection. Traffic from programs that don't use mDNSResponder (e.g. dig) is still intercepted as on previous versions of macOS, but the majority of the DNS traffic will go through mDNSResponder.
This makes the DNSProxyProvider all but useless for our purpose. Is there a new way to configure the DNSProxyProvider? It feels like an oversight since the stated purpose of the DNSProxyProvider is to "intercept all DNS traffic generated on the device" (quoting the current documentation). Is there any new documentation on the topic of DNS behavior in Ventura? We did not find any in the release notes.
Thank you.
Post
Replies
Boosts
Views
Activity
I have a host app embedding a system extension and a setup in which the bundle identifier for both is defined by expanding a variable defined in an .xcconfig file. However, when I use different configurations with different configuration files, the "Embed System Extension" step in the build phases of the host app target does not seem to get updated and tries to copy a file with a constant name, regardless of which configuration is used.
How can I embed a system extension whose bundle identifier is expanded from an .xcconfig build variable?
Steps to reproduce
Create a new xcode project (with development team "None") and add a system extension to be embedded. I am using a network extension.
Add configuration files configA.xcconfig and configB.xcconfig setting the variable BUNDLE_ID to com.example.a and com.example.b respectively.
Duplicate the Debug configuration under Project > Info, name them DebugA and DebugB, and set them up to use the configA and configB files for the whole project.
Create two schemes SchemeA and SchemeB for the main target and set them up to run the DebugA and DebugB configurations under "Run".
In the build settings, set the bundle identifier of the host app to $(BUNDLE_ID) and the bundle identifier of the extension to $(BUNDLE_ID).Extension
When I do this, SchemeA will run successfully while SchemeB fails building. Looking at the build log I can see that it successfully builds the com.example.b.Extension.systemextension product file but when it gets to embedding the system extension in the host app using PBXCp it tries to copy a file with the wrong name based on the bundle id value of the other configuration, hence
error: /Users/username/Library/Developer/Xcode/DerivedData/EmbedSysExt-caqirriqcwynzmdqqzniwgvzhziq/Build/Products/DebugB/com.example.a.Extension.systemextension: No such file or directory (in target 'ProjectName' from project 'ProjectName')
I am happy to provide a minimal example xcode project created using the steps above, however I am not sure how to make it available.
Some investigation
One way around this (which would be ok in my case) would be to give the system extension a constant product name in the build settings, regardless of configuration. Then the file to be copied has a fixed name so PBXCp will always copy the right thing. However, a system extension won't activate if this product name is different from the bundle id, as e.g. reported here:
https://developer.apple.com/forums/thread/133499?answerId=421984022#421984022
I think it would be possible to use a constant product name and add a run script phase that will rename the folder after it has been copied. I would consider that quite hacky.
Searching for com.example.a in files on disk I can see that the bundle identifier is repeated literally (i.e. not using $(BUNDLE_ID)) inside the project.pbxproj file. Changing it manually in that file, however, does not seem to have any effect.
Cleaning the build folder and/or closing and restarting xcode didn't seem to have any effect.
As I understand the layout process in SwiftUI, most parent views by default only take up as much space as they need to fit their children. The following minimal example content view shows how the window will grow and shrink in response to populating a VStack with Text Views:
struct ContentView: View {
@State var texts = ["text"]
var body: some View {
VStack {
Button("Add text") {
texts.append("text")
}
Button("Remove text") {
_ = texts.popLast()
}
ForEach(texts, id: \.self) { text in
Text(text)
}
}
}
}
Using the above ContentView, the window can't be manually resized.
A TabView, however, doesn't seem to adhere to this behavior. When wrapping the VStack above in a TabView, the window will be manually resizable and the tab view always takes up the whole window. In particular, It doesn't grow if the VStack grows too large and it doesn't shrink when the VStack shrinks.
struct ContentView: View {
@State var texts = ["text"]
var body: some View {
TabView {
VStack {
Button("Add text") {
texts.append("text")
}
Button("Remove text") {
_ = texts.popLast()
}
ForEach(texts, id: \.self) { text in
Text(text)
}
}
.tabItem { Text("tabItem") }
}
}
}
I would like to have a TabView that behaves just like the VStack above, expanding and contracting in height based on its contents, and likewise causing the whole window to grow and shrink accordingly.
Despite two hours of internet research I have failed to understand what determines this difference in behavior and how to fix it.
It is likely important to note that I am on macOS 10.15.
To make the example content views work I have placed them in ContentView.swift of the template generated by Xcode for a macOS app with Interface set to "SwiftUI" and Life Cycle set to "AppKit App Delegate".
TL;DR How to make this question - https://developer.apple.com/forums/thread/72881's answer work within the sandbox? Is there a good way to make calling SecCodeCopyGuestWithAttributes possible from within a sandbox?
We are developing a macOS system extension (more precisely a DNSProxy Network Extension) that is providing an NSXPCListener which is communicating with its host app via XPC, just as in the SimpleFirewall - https://developer.apple.com/videos/play/wwdc2019/714 code example from WWDC 2019.
Both the host app and the system extension are sandboxed and share an app group, and this is a requirement as we want to be accepted into the mac app store.
As we believe it is good practice to keep potential attack surfaces small, we would like to make sure that only our host app can connect to the system extension via XPC.
There are numerous posts here on what exactly to do, most helpful is this one - https://developer.apple.com/forums/thread/72881 which inspired this question's title. The crucial step for this question is to get the connecting process' code object through a call to SecCodeCopyGuestWithAttributes by process id, which is provided in NSXPCListenerDelegate.listener(_, shouldAcceptNewConnection). From within the sandbox this returns an OSStatus of 100001, which is some general "Operation not permitted" and in the console I can see an error logged by kernel where the sandbox denies read access to the .app file of the connecting app:
error 14:12:13.946693+0200 kernel Sandbox: bundleidentifier(33545) deny(1) file-read-data /Users/name/Library/Developer/Xcode/DerivedData/projectworkspace/Build/Products/Debug/appname.app
If I disable the sandbox, or add a temporary exception entitlement to allow read access to the Xcode/DerivedData folder then I can retrieve the code object, so I conclude that indeed the sandbox blocking the read of the file is the problem.
The obvious fix to me seems to be to add a com.apple.security.temporary-exception.files.absolute-path.read-only entitlement for /Applications/appname.app/, but this a) might be frowned upon in the app review and b) seems somewhat brittle. For example, it would prevent the app from working from inside the user's application folder, and needs a separate entitlement to work from the Xcode debugger.
Since making sure that your inter process communication is only open to a few trusted apps seems like a fairly standard precaution, I would like to ask whether there is a better option, or a recommended way to secure your XPC against unwanted connections from within a sandbox?
P.S. 1: This fairly recent question - https://developer.apple.com/forums/thread/671488 is very similar, however it remains without a satisfying conclusion and I hope to provide a better solution for the benefit of future investigators already inside this question.
P.S. 2: The fact that NSXPCListener is running inside a system extension rather than any other "normal" app in the same app group is likely not relevant, just note that the system extension is running as root while the host app is running as the normal user. This might for example make this bug - https://developer.apple.com/forums/thread/127779?answerId=423311022#423311022 (r. 63976204) relevant as we are targeting Catalina (macOS 10.15), but we are already using the entitlement necessary for a workaround for unrelated reasons.
P.S. 3: For the purposes of this question I am ignoring issues with using the process identifier, like the ones linked here - https://developer.apple.com/forums/thread/72881?answerId=382674022#382674022
We are developing an application to filter and block DNS traffic on macOS based on the NEDNSProxyProvider API and would like to comply with Apple's best practices, as well as be eligible for the App Store.
Currently, we understand the lifecycle of our extension as follows:
First, the host app “installs” the network extension with an OSSystemExtensionRequest.activationRequest. After the user confirms the request and it succeeds, this starts the extension and executes the code in main.swift in the extension target, like in the SimpleFirewall example application - https://developer.apple.com/videos/play/wwdc2019/714. DNS traffic is not yet being routed to the proxy, however. For this post I would like to refer to the part that is running now as the “system extension” and the part that is not yet running as the “DNS proxy”.
To start the DNS proxy, you now need to enable it in the DNSProxyManager and save this to preferences.
If the user wants to enable/disable DNS blocking from a setting in the host app, we imagine that they will be enabling/disabling the DNS proxy through DNSProxyManager. This leaves the system extension running.
Is it indeed the intended practice to leave the system extension running and performing basic tasks even while the DNS proxy is disabled, or is this “abusing” the system extension? Such basic tasks could for example include occasional communication with the backend server, and leaving the XPC listener for communication with the host app running and would hence still use (some) system resources even though the user has decided to disable the functionality.
Is the system extension guaranteed to keep running indefinitely and to be launched at reboot, even if the DNS proxy remains disabled?
Is it even possible to shut down the system extension completely, without requiring a restart (like OSSystemExtensionRequest.deactivationRequest might)?
I am trying to find ways to start my macOS application (a network extension to filter traffic) automatically for all users, and preferably at system start even before a user logs in.
I can find a lot of references to SMLoginItemSetEnabled, which adds the application to the user's LoginItems so that it is started for the specific user after they log in, but nothing that holds system-wide.
As far as I can see, applications that start up automatically for all users place their .plist in either the LaunchAgents or the LaunchDaemon folders in /Library/ or /System/Library/.
What is the correct/recommended way to start a network extension automatically and as early as possible for all users? Is this permissible for an application in the mac App Store (assuming explicit user consent)?
Thank you