Example of DNS Proxy Provider Network Extension

I am trying to setup a system-wide DNS-over-TLS for iOS that can be turned off and on from within the app, and I'm struggling with the implementation details. I've searched online, searched forums here, used ChatGPT, and I'm getting conflicting information or code that is simply wrong. I can't find example code that is valid and gets me moving forward.

I think I need to use NEDNSProxyProvider via the NetworkExtension. Does that sound correct? I have NetworkExtension -> DNS Proxy Capability set in both the main app and the DNSProxy extension.

Also, I want to make sure this is even possible without an MDM. I see conflicting information, some saying this is opened up, but things like https://developer.apple.com/documentation/Technotes/tn3134-network-extension-provider-deployment saying a device needs to be managed. How do private DNS apps do this without MDM?

From some responses in the forums it sounds like we need to parse the DNS requests that come in to the handleNewFlow function. Is there good sample code for this parsing?

I saw some helpful information from Eskimo (for instance https://developer.apple.com/forums/thread/723831 ) and Matt Eaton ( https://developer.apple.com/forums/thread/665480 )but I'm still confused.

So, if I have a DoT URL, is there good sample code somewhere for what startProxy, stopProxy, and handleNewFlow might look like? And valid code to call it from the main app?

Answered by DTS Engineer in 860650022

If you want to support the DNS-over-TLS standard — RFC 7858 and friends — then you don’t need a DNS proxy provider. Rather, you can configure the system to use its built-in DNS-over-TLS implementation using the Network Extension DNS settings API.

DNS proxy providers are useful when the DNS server speaks a custom protocol. And creating such a provider is way more complex than applying custom DNS settings. Moreover, such providers have significant deployment limitations, as described in TN3134 Network Extension provider deployment. My advice is that you avoid this path if at all possible.

Share and Enjoy

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

If you want to support the DNS-over-TLS standard — RFC 7858 and friends — then you don’t need a DNS proxy provider. Rather, you can configure the system to use its built-in DNS-over-TLS implementation using the Network Extension DNS settings API.

DNS proxy providers are useful when the DNS server speaks a custom protocol. And creating such a provider is way more complex than applying custom DNS settings. Moreover, such providers have significant deployment limitations, as described in TN3134 Network Extension provider deployment. My advice is that you avoid this path if at all possible.

Share and Enjoy

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

Thank you for the quick response! In the information I came across before asking this question, it sounded like a toggle to turn system wide DNS on or off from within the app was impossible without the Network Extension? So just to clarify, will the DNS Settings API allow it to be turned on and off from within the app?

Accepted Answer
will the DNS Settings API allow it to be turned on and off from within the app?

Sure. In the DNS settings API you use NEDNSSettingsManager to apply your settings, and it has an isEnabled property that does what you want.

IMPORTANT The limitations on DNS proxy providers are privacy focused. A DNS proxy has a privileged network position, allowing on-device code to see all the DNS names used by the user. The DNS settings API doesn’t work that way, because you just apply settings that are then used by the built-in resolver. That’s why you can use the DNS settings API in any context, where DNS proxy providers have hard deployment limits.

Share and Enjoy

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

We have an initial version working now, thank you!

Well, it looks like I may have spoken too soon. The isEnabled property on NEDNSSettingsManager appears to be read only. Which means that we cannot flip .isEnabled ourselves from the app, which would mean that the user has to select it in Settings. And that property even says, "DNS settings must be enabled by the user in Settings or System Preferences."

(The "working" version I mentioned was someone going into Settings and turning it on manually.)

We need the ability to turn it on and off from within the app. Am I missing something?

Actually, slight Edit. We are ok with going in to iOS Settings once when it's first loaded to switch to our DoT settings, but after that we'd like to be able to turn it on and off from within the app and haven't yet figured out how to do that without access to the isEnabled property on NEDNSSettingsManager. We can remove it altogether. But then we have to go back into Settings when we re-load it to enable it, which we are trying to avoid (and other apps seem to be able to disable and enable from within the app).

The isEnabled property on NEDNSSettingsManager appears to be read only.

D’oh! I missed that.

In my defence, it’s read/write for all the other provider types. I’m not sure why it’s read-only just in this case.

The obvious workaround is to remove the configuration but, as you noted, that’ll trigger another round of user approval when you reinstall the configuration.

Share and Enjoy

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

Sigh, ok, so we are back to square one. So then it seems we would have to implement DNS Proxy to do what we want? And it seems that other providers like NextDNS probably use this if they have DNS not connected to their VPN but can turn it on and off from within the app?

Or do you have other thoughts now on how to accomplish this? Thanks!

We do already have a Network Extension for VPN, but we'd prefer to be able to enable/disable our DoT without/outside the VPN if possible (ie they would be separate functions).

So then it seems we would have to implement DNS Proxy to do what we want?

I don’t think that’ll actually work. Because of the DNS proxy deployment limitations [1], the ability to modify the proxy configuration is limited to development-signed apps. Once you sign your app for distribution, it’ll lose its ability to modify its DNS proxy configurations. Rather, the site admin is expected to configure the proxy using MDM payloads.

Or at least that’s how it works with content filters. I can’t remember whether I’ve ever actually tested this with DNS proxies. However, both occupy a similarly privileged networking position and so I expect them to follow the same rules.

A DNS proxy can (more or less) go into pass through mode by leaning in to the systemDNSSettings property.

Or do you have other thoughts now on how to accomplish this?

I’ve not explored the DNS settings API in depth, but there seems like a lot of potential options you could play around with. For example:

  • I’m not sure what happens if you set dnsSettings to nil.
  • You could set matchDomains so that your configuration only applies to a specific domain.
  • You could supply a bad DNS server and set allowFailover to true.

Of course, these all assume that saveToPreferences() doesn’t trigger another authentication. I think that’s the case, based on my experience with other NE APIs, but I’ve not actually tried it.

Let us know how you get along.

Share and Enjoy

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

[1] On iOS. On macOS, this isn’t the case.

matchDomains did it! In order to disconnect, I set matchDomains to a bogus entry that should never be used, and the other domains went back to using the default resolver. This allows us to switch it "on" and "off" from within the app. Thank you!

I can't seem change the correct answer once I selected it, but your last post should be the correct answer.

Example of DNS Proxy Provider Network Extension
 
 
Q