How can I programmatically access the NETunnelProviderManager of a Per-App VPN?

I have an iOS app which contains a Network Extension that subclasses the NEPacketTunnelProvider, acting as a packet-tunnel VPN. After deploying the app on the device as a regular app, it runs the following code fragment:

NETunnelProviderManager.loadAllFromPreferences { managers, _ in
  self.manager = managers?.first ?? NETunnelProviderManager()
  self.manager.protocolConfiguration = getConfiguration()
  self.manager.saveToPreferences { error in
    // Handle errors or show a "Connect" button in the UI
  }
}

This asks the user to install the extension as a "Device VPN". I can then use try? self.manager?.connection.startVPNTunnel() to start the VPN (and later stop it when needed). So far, this works fine.

Now, I want to deploy the app with an MDM and set it up as the "custom VPN" of a "Per-App VPN". I have tested the setup using

  1. a real MDM, AND
  2. using the "development" setup described in NETunnelProviderManager.

In both cases, the "Per-App VPN" shows up as a VPN in the "Settings" app.

However, in both cases I am unable to retrieve, configure or use the "Per-App VPN". The code fragment posted above returns no NETunnelProviderManager at all. When instantiating one on my own and triggering self.manager.saveToPreferences(), it queries the user to install a "Device VPN". While I can control and use the latter, this is clearly not what I want after having gone through the pain of installing the "Per-App VPN".

How can I retrieve the NETunnelProviderManager of the "Per-App VPN"? And then use it to configure and control the VPN connection? (Ideally, I would like to use the same app and the same Network Extension for both use cases, leaving the choice of which VPN type to use to the user or the user's MDM administrator.)

We’re talking iOS here, right?

Share and Enjoy

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

Yes, as I wrote above I refer to an "iOS app". Which means I cannot use the forAppVPN() static method which is unavailable on that platform.

Yes, as I wrote above I refer to an "iOS app".

Indeed. Sometimes I can’t see the wood from the trees. Sorry )-:

On iOS, per-app VPN is only deployable via MDM [1]. Given that, it’s rare for users to want to run the container app because they can’t do anything useful in it. The VPN is configured by their admin.

So what sort of configuration are you expecting to do in your app?

Share and Enjoy

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

[1] Because you need MDM to set the VPNUUID property on the app in order to match the VPNUUID property in the VPN configuration.

In the current situation the container app is used to personalize the VPN connection (i.e., it receives additional payload data from the admin). For security reasons this additional data MUST NOT be provided by the MDM. (This is a legal requirement we cannot work around.)

Additionally, the container app shows status information from the Network Extension that goes beyond connected/disconnected. According to my understanding, that's precisely what provider configurations, and sendProviderMessage of NETunnelProviderSession are for.

Now, I could try to mimic functionality with shared control files or shared keychain entries, but this makes the architecture more complex and most likely also more fragile. My expectation is that choosing a "Per-App VPN" over a "Device VPN" makes things more secure and more robust.

Update: I just noticed that in https://developer.apple.com/forums/thread/128502 you wrote that "Your app can present a UI to control your VPN but there’s no requirement to do so", so it seems to be possible to control the connection from the app. How can it be done without the NETunnelProviderManager of the container app?

I just noticed that in …

It’s hard to recall the exact context but I suspect that I just assumed that this’d work. Given that you’ve evidence to the contrary, it might be time to revisit that assumption O-:

In the current situation the container app is used to personalize the VPN connection

So how does that work at the user level? Is the user expected to run your container app before the VPN is functional?

Share and Enjoy

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

How can I programmatically access the NETunnelProviderManager of a Per-App VPN?
 
 
Q