Implementing a network filter on an iOS device
With the release of iOS 9 in September 2015, Apple added a new functionality to the Network Extension framework. This feature enables an application to monitor and filter the entire network traffic from all the applications installed on the device.
As an iOS developer, Apple fan and tech enthusiast, this seemed scary at first. After doing a little research on the subject, I found out that this feature was specifically designed for schools and enterprises that manage multiple company-owned devices. In addition, this functionality is only available for supervised devices. It also requires a configuration profile that should be installed by the user in the Settings app.
Keep on reading if you want to uncover the basics of content filter providers. In this blogpost I will also show you how to build a simple example app. Those of you interested in making your own network traffic monitor or filter, read on for a step-by-step guide that will walk you through the whole process.
Main point
In order to add the content filter capability to an iOS application, one will need to add two extensions to it: a filter data provider and a filter control provider.
The filter data provider extension is a subclass of NEFilterDataProvider, which decides if a network flow should be blocked or allowed.
The filter control provider extension is a subclass of NEFilterControlProvider which passes configuration information to the filter data provider in order to allow that provider to function correctly.
The decision to split this functionality into two separate extensions was taken having in mind some privacy concerns. Hence, the filter data provider has access to the entire user network content, but runs in a very restrictive sandbox which does not allow it to make network calls or write data to disk. The sandbox of the filter control provider is not as restrictive, but does not have access to the user data.
Creating the app
Open xCode and create a new swift iOS application. For the purpose of this tutorial, I named the app NetworkFilterDemo having the bundle identifier com.x2mobile.NetworkFilterDemo (we will need this to create a configuration profile later on).
Once the application is created, the first thing you need to do is to enable Network Extensions capabilities. You can do this by selecting the project from the navigator, then select Signing & Capabilities and click on + Capability.
Select Network Extensions from the available capabilities list, which will cause a new section to appear below the Signing section in Signing and Capabilities. Make sure that you select Content Filter as shown in the screenshot below.
Next, you need to create the Filter Data Provider and Filter Control Provider extensions. In order to do that, you will have to click on the project in the navigator and hit “+” under the target list.
Select the Network Extension template from the iOS section and click Next. Enter a name (I named it DataFilter) and make sure you select Filter Data under Provider Type, then hit finish. Finally, add another Network Extension, but this time select Filter Control under Provider Type (I named it ControlFilter).
After completing these steps, your project structure should have two new folders: one for the DataFilter and another for ControlFilter. Each folder will contain a swift file which will be a subclass of NEFilterDataProvider and NEFilterControlProvider, along with a .plist file and entitlements file.
Once all the above mentioned steps are finished, you can follow the instructions which will help you create a supervised device and a configuration profile in order to be able to run and test the network filter on a real device.
Supervise a device
Open Apple Configurator.
Select All Devices and from the list select the device you want to supervise, then click prepare.
3. Select manual configuration and make sure Supervise devices is also selected, then click Next.
4. In the next step select Do not enroll in MDM and hit Next.
5. You are then required to sign in to Apple School Manager or Apple Business Manager. You have two options: log in if you already have one of these accounts, or hit Skip and create a new organization. For the purpose of this tutorial I’ve created a new dummy organization under the name of DummyOrganization.
6. Finally, select the steps that will be presented to the user in Setup Assistant and click Prepare.
Creating and installing the configuration profile
Firstly, before starting the process of creating the configuration profile, you need to export a distribution certificate from Keychain Access.
In order to do this, you will have to open Keychain Access, find your distribution certificate, then click on expand and select the certificate along with the private key associated with it. Right click on the selections, choose export and save it as a .p12 format in a known location (after you’ve created a password for it).
Once you have your .p12 file, open Apple Configurator 2 and go to File -> New Profile. In the profile creation window that popped-up, go to the General section and enter the name of the profile. I chose to name it Network Filter Profile, but you can name it however you want to.
Next, go to the Certificates section. Move to the right side of the screen and click on Configure. Select your .p12 file from the location you’ve previously saved it and click Open. Enter the password for the certificate and tap enter.
Once you’ve added the certificate, go to the Content Filter section from the left hand side of the profile creation window and hit Configure (as described above). Select Plugin for Filter Type, name it, enter the bundle id of the application you’ve just created (not the extensions) and select the certificate you’ve added in the previous step from the drop-down as shown in the screenshot below.
Finally, go to File -> Save and save the profile in a known location.
In order to install the configuration profile on your already supervised device, you just need to send it to the device via AirDrop, email or any other means available. The iOS will guide you on how to install it.
Running and testing your application
To test your application, go into your NEFilterDataProvider subclass from your Data Provider extension. Inside handleNewFlow(_:) function, change the default implementation to always return .needsRule() and add a breakpoint to that line. Telling the iOS that the Data Provider needs more rules will cause the operating system to call the handleNewFlow(_:completionHandler:) function from your NEFilterControlProvider subclass in your Control Provider extension. Finally, add a breakpoint in handleNewFlow(_:completionHandler:) function at the line which calls the completion handler.
Before running, your application should look something like this:
Lastly, run your application on the supervised device. In xCode, go to Debug -> Attach To Process and select the Data Provider extension from the list. Do the same for the Control Provider extension. From there, you can go to Safari or any other application which makes network calls on your supervised device and use it to make a network call. Once you do that, you will see that your xCode enters first in your Data Provider breakpoint and after you click continue in your Control Provider breakpoint.
Final observations and possible use cases
One more thing that I would like to mention refers to the communication between the two extensions. Since they both run in a restrictive sandbox, the only way one can send information to another is through a shared UserDefaults instance by enabling the App Group capability on both extensions.
Unfortunately, the scope of this tutorial ends here, but one can very easily make a network filter app, an ad blocker or a network monitor using this skeleton and the basic principles laid out here.