Is NEDNSProxyProvider supported on macOS?

Is NEDNSProxyProvider supported on macOS?

According to this page

Availability
iOS 11.0+
macOS 10.15+
Mac Catalyst 13.0+

But on this page it says

DNS proxy providers are only supported on supervised iOS devices.

I tested a DNS proxy application on the mac which runs well on iOS devices(I made some project settings change such as profiles, platform settings) and got following error in the console logs
Code Block
Looking for an extension with identifier com.blob.macappproxy.dns and extension point com.apple.networkextension.dns-proxy
Failed to find an app extension with identifier com.blob.macappproxy.dns and extension point com.apple.networkextension.dns-proxy: (null)

And in the network preferences there is a DNS proxy service added but not running, it shows a message on the panel says

Please use "macappproxy" to control this DNS proxy configuration.

Can someone tell me what's the cause of this error and how can I fix it? Thanks a lot!

Answered by Systems Engineer in 667019022
@sadcat

FYI I have SIP disabled.

First enable SIP. This will always hide problems that are lurking in your configuration.


Regarding your code sample, it looks like you are using NEFilterManager and NEDNSProxyProviderProtocol together.
I posted a code snippet of setting up and starting a NEDNSProxyProvider from a container app below. Note that most of the code here is elided except for the core piece:
Code Block swift
class ViewController: NSViewController {
var manager: NEDNSProxyManager?
private func installSystemExtension() {
let request = OSSystemExtensionRequest.activationRequest(
forExtensionWithIdentifier: "bundle.id.to.your.NEDNSProxyProvider",
queue: .main
)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
}
private func configureProxy() {
guard let dnsManager = manager else { return }
dnsManager.loadFromPreferences { error in
precondition(Thread.isMainThread)
if let nsError = error as NSError? {
/* Handle error */
return
}
let proto = NEDNSProxyProviderProtocol()
proto.serverAddress = "localhost" /* Just for testing purposes */
proto.providerBundleIdentifier = "bundle.id.to.your.NEDNSProxyProvider"
dnsManager.providerProtocol = proto
dnsManager.isEnabled = true
dnsManager.localizedDescription = "Testing DNS Proxy"
dnsManager.saveToPreferences { saveError in
if let nsError = saveError as NSError? {
/* Handle error */
return
}
/* Handle Success Case */
}
}
}
}
extension ViewController: OSSystemExtensionRequestDelegate {
/* Other delegate methods here */
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
switch result {
case .completed:
manager = NEDNSProxyManager.shared()
case .willCompleteAfterReboot:
/* Proceed with result here */
@unknown default:
/* Proceed with result here */
}
}
}


Hope this helps.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Is NEDNSProxyProvider supported on macOS?

Yes, starting with macOS 10.15.

But on this page it says

This needs to be updated (this was written before the macOS 10.15 change and was correct at the time). We’re hoping to get that done sooner rather than later.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
I am getting the same errors as you jeffreyc.

Does anyone know what might be causing this or have sample code of a functional DNS Proxy using NEDNSProxyProvider?

Any help would be appreciated!
Using the jeffreyc's error as an example:

Code Block
Looking for an extension with identifier com.blob.macappproxy.dns and extension point com.apple.networkextension.dns-proxy
Failed to find an app extension with identifier com.blob.macappproxy.dns and extension point com.apple.networkextension.dns-proxy: (null)


This typically points to a configuration issue when loading the Network System Extension. Take a look at how you are configuring and loading the OSSystemExtensionRequest.activationRequest.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thanks meaton. Unfortunately I have made little progress.

After I call OSSystemExtensionManager.shared.submitRequest(activationRequest) I get System extension request failed: Invalid code signature or missing entitlements from:

Code Block
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
     os_log("System extension request failed: %@", error.localizedDescription)
status = .stopped
}


I am not sure why/if the code signature is invalid. I have looked through my Mac App's entitlements and can't see anything missing. FYI I have SIP disabled.

I have used this sample code as a reference for my project which is currently barebones as it is in a proof of concept stage:
https://developer.apple.com/documentation/networkextension/filtering_network_traffic

Here is where I am trying to submit the activation request:
Code Block
@IBAction func startFilter(_ sender: Any) {
    status = .indeterminate
    guard !NEFilterManager.shared().isEnabled else {
      registerWithProvider()
      return
    }
    guard let extensionIdentifier = extensionBundle.bundleIdentifier else {
      self.status = .stopped
      return
    }
    // Start by activating the system extension
    let activationRequest = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: extensionIdentifier, queue: .main)
//    NSLog("bundleIdentifier: " + extensionIdentifier)
    activationRequest.delegate = self
    OSSystemExtensionManager.shared.submitRequest(activationRequest)
     
    self.update {
      self.manager.localizedDescription = "DNS Proxy"
      let proto = NEDNSProxyProviderProtocol()
      // proto.providerConfiguration = +++
      let data: [String: Any] = [
        "key1": "example value 1",
        "key2": "example value 2",
        "items": []
      ]
      proto.providerConfiguration = data
      proto.providerBundleIdentifier = extensionIdentifier
      self.manager.providerProtocol = proto
      self.manager.isEnabled = true
    }
  }


Any ideas?
Accepted Answer
@sadcat

FYI I have SIP disabled.

First enable SIP. This will always hide problems that are lurking in your configuration.


Regarding your code sample, it looks like you are using NEFilterManager and NEDNSProxyProviderProtocol together.
I posted a code snippet of setting up and starting a NEDNSProxyProvider from a container app below. Note that most of the code here is elided except for the core piece:
Code Block swift
class ViewController: NSViewController {
var manager: NEDNSProxyManager?
private func installSystemExtension() {
let request = OSSystemExtensionRequest.activationRequest(
forExtensionWithIdentifier: "bundle.id.to.your.NEDNSProxyProvider",
queue: .main
)
request.delegate = self
OSSystemExtensionManager.shared.submitRequest(request)
}
private func configureProxy() {
guard let dnsManager = manager else { return }
dnsManager.loadFromPreferences { error in
precondition(Thread.isMainThread)
if let nsError = error as NSError? {
/* Handle error */
return
}
let proto = NEDNSProxyProviderProtocol()
proto.serverAddress = "localhost" /* Just for testing purposes */
proto.providerBundleIdentifier = "bundle.id.to.your.NEDNSProxyProvider"
dnsManager.providerProtocol = proto
dnsManager.isEnabled = true
dnsManager.localizedDescription = "Testing DNS Proxy"
dnsManager.saveToPreferences { saveError in
if let nsError = saveError as NSError? {
/* Handle error */
return
}
/* Handle Success Case */
}
}
}
}
extension ViewController: OSSystemExtensionRequestDelegate {
/* Other delegate methods here */
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
switch result {
case .completed:
manager = NEDNSProxyManager.shared()
case .willCompleteAfterReboot:
/* Proceed with result here */
@unknown default:
/* Proceed with result here */
}
}
}


Hope this helps.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thanks for your assistance but I am still having issues and will continue to investigate with SIP enabled as you suggested. In the meantime this is what I have.

ViewController.swift
Code Block
import Cocoa
import os
import NetworkExtension
import SystemExtensions
class ViewController: NSViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    os_log("DNSFILTER: viewDidLoad")
    installSystemExtension()
    configureProxy()
  }
  override var representedObject: Any? {
    didSet {
    // Update the view, if already loaded.
    }
  }
   
  var manager: NEDNSProxyManager?
  private func installSystemExtension() {
    os_log("DNSFILTER: installing system extension")
    let request = OSSystemExtensionRequest.activationRequest(
      forExtensionWithIdentifier: "com.blob.macappproxy.dns",
      queue: .main
    )
    request.delegate = self
    OSSystemExtensionManager.shared.submitRequest(request)
  }
   
  private func configureProxy() {
    os_log("DNSFILTER: configuring proxy")
    guard let dnsManager = manager else { return }
    dnsManager.loadFromPreferences { error in
      precondition(Thread.isMainThread)
       
      if let nsError = error as NSError? {
        os_log("DNSFILTER: Failed to load the filter configuration: %@", nsError.localizedDescription)
        return
      }
      let proto = NEDNSProxyProviderProtocol()
      proto.serverAddress = "localhost" /* Just for testing purposes */
      proto.providerBundleIdentifier = "com.blob.macappproxy.dns"
      dnsManager.providerProtocol = proto
      dnsManager.isEnabled = true
      dnsManager.localizedDescription = "Testing DNS Proxy"
       
      dnsManager.saveToPreferences { saveError in
        if let nsError = saveError as NSError? {
          os_log("DNSFILTER: Failed to disable the filter configuration: %@", nsError.localizedDescription)
          return
        }
        /* Handle Success Case */
        os_log("DNSFILTER: dns proxy configured")
      }
    }
  }
}
extension ViewController: OSSystemExtensionRequestDelegate {
  func request(_ request: OSSystemExtensionRequest, actionForReplacingExtension existing: OSSystemExtensionProperties, withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
    os_log("DNSFILTER: Replacing extension %@ version %@ with version %@", request.identifier, existing.bundleShortVersion, ext.bundleShortVersion)
    return .replace
  }
   
  func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
    os_log("DNSFILTER: Extension %@ requires user approval", request.identifier)
  }
   
  func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
    os_log("DNSFILTER: System extension request failed: %@", error.localizedDescription)
  }
   
  /* Other delegate methods here */
  func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
    switch result {
    case .completed:
      manager = NEDNSProxyManager.shared()
    case .willCompleteAfterReboot:
       os_log("DNSFILTER: willCompleteAfterReboot")
    @unknown default:
      os_log("DNSFILTER: default")
    }
  }
}


Code Block
2021-03-18 17:09:27.676100+1100 macappproxy[1142:14431] DNSFILTER: viewDidLoad
2021-03-18 17:09:27.676187+1100 macappproxy[1142:14431] DNSFILTER: installing system extension
2021-03-18 17:09:27.678794+1100 macappproxy[1142:14431] DNSFILTER: configuring proxy
2021-03-18 17:09:27.922090+1100 macappproxy[1142:14431] DNSFILTER: System extension request failed: The operation couldn’t be completed. (OSSystemExtensionErrorDomain error 1.)


When configureProxy() is invoked it doesn't get past line 36 in ViewController.swift, which I guess is understandable if the extension hasn't started.



My DNSProxyProvider.swift is barebones:
Code Block
import NetworkExtension
import os
class DNSProxyProvider: NEAppProxyProvider {
  override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {
    // Add code here to start the process of connecting the tunnel.
    os_log("DNSFILTER: provider started")
completionHandler(nil)
  }
   
  override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
    // Add code here to start the process of stopping the tunnel.
    completionHandler()
  }
   
  override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
    // Add code here to handle the message.
    if let handler = completionHandler {
      handler(messageData)
    }
  }
   
  override func sleep(completionHandler: @escaping () -> Void) {
    // Add code here to get ready to sleep.
    completionHandler()
  }
   
  override func wake() {
    // Add code here to wake up.
  }
   
  override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
    // Add code here to handle the incoming flow.
    return false
  }
}


Let me know if anything looks awry. Thanks. I will continue to trawl through the console logs for any clues.
@sadcat I would open a TSI here for extended help on your issue. That way myself or Quinn can take a closer look.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
@meaton I have done that. I eagerly await a response.
@SadCat, I picked up your TSI and it is in my queue. Thank you.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
I wanted to update this thread that the distribution information for DNS Proxy Provider has now been updated.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Is NEDNSProxyProvider supported on macOS?
 
 
Q