Best practices for blocking traffic: Transparent Proxy vs Content Filter, and Multiple Network Extensions

Hello,

I am working on a DLP system for macOS. The application must analyze and potentially block network traffic. Currently, I am using NETransparentProxyProvider for traffic analysis and looking for the best solution to block network traffic. I believe the blocking can be implemented by capturing the flow and choosing not to proxy/forward it further (dropping it). However, I am unsure how the OS and other applications will react to this approach. As far as I know, macOS provides a dedicated NEFilterDataProvider (Content Filter) specifically designed for traffic filtering and blocking.

I have two questions regarding the architecture:

  1. Is it safe to block flows directly using a Transparent Proxy? Will dropping/ignoring the flow without forwarding it cause any unexpected system behavior, socket hangups, or performance issues? Or is it highly recommended to strictly use a Content Filter (NEFilterDataProvider) for the blocking aspect?
  2. Can a single container app install and manage more than one Network Extension in the system? For example, if the best practice dictates separating the logic, can my single DLP macOS application bundle and install both a Transparent Proxy (for analysis/routing) and a Content Filter (for blocking) simultaneously?

Thank you in advance!

Answered by DTS Engineer in 888637022

If you haven’t already done so, I recommend that watch WWDC 2025 Session 234 Filter and tunnel network traffic with NetworkExtension. Alice gives a good overview of the expected use cases for this stuff.

Regarding your specific questions, lemme tackle the second one first:

2- Can a single container app install and manage more than one Network Extension in the system?

Yes.

There are actually two ways to slice this:

  • Put each provider in a separate sysex.
  • Create a single sysex with multiple providers.

Additionally, you can combine different sysex types into a single sysex. For example, folks often combine an ES client and NE providers into one sysex.

My general advice is that you use as few sysexen as possible. Managing sysexen is a bit of a pain, so life is easier if you have just one.

If your sysex has multiple providers, you can enable and disable them independently by enabling or disabling each provider configuration using its corresponding NEXyzManager type.

Is it safe to block flows directly using a Transparent Proxy?

“Safe” is such a tricky word (-:

There are two really significant differences between a content filter and a transparent proxy:

  • A transparent proxy can modify the flow; a content filter cannot.
  • Once a transparent proxy has accepted a flow, it must proxy that flow until the flow ends. A content filter can examine part of the flow and, once it’s satisfied, return an .allow() verdict. At that point the flow’s traffic no longer comes to the filter, so it’s out of the loop.

If you decide to do filtering in your transparent proxy, it’s best not to ignore a flow that you’ve decided to drop. That produces a black hole, which can cause all sort of weird behaviour. Rather, you have to force the flow to close by calling one the close methods.

Finally, performance. All NE provider have a measurable performance impact. The fewer you add, the less that is. If you need a transparent proxy for other reasons, it’s reasonable to do your filtering there. However, you have to balance that against the performance impact of not being able to get out of the loop with an .allow() verdict.

As with many performance questions, it’s hard to offer a good answer based on theory alone. I recommend that you create a prototype, measure with realistic workloads, and then use that to set your path forward.

And if you’d like to share those results here, I’d be most interested in seeing them (-:

Share and Enjoy

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

Accepted Answer

If you haven’t already done so, I recommend that watch WWDC 2025 Session 234 Filter and tunnel network traffic with NetworkExtension. Alice gives a good overview of the expected use cases for this stuff.

Regarding your specific questions, lemme tackle the second one first:

2- Can a single container app install and manage more than one Network Extension in the system?

Yes.

There are actually two ways to slice this:

  • Put each provider in a separate sysex.
  • Create a single sysex with multiple providers.

Additionally, you can combine different sysex types into a single sysex. For example, folks often combine an ES client and NE providers into one sysex.

My general advice is that you use as few sysexen as possible. Managing sysexen is a bit of a pain, so life is easier if you have just one.

If your sysex has multiple providers, you can enable and disable them independently by enabling or disabling each provider configuration using its corresponding NEXyzManager type.

Is it safe to block flows directly using a Transparent Proxy?

“Safe” is such a tricky word (-:

There are two really significant differences between a content filter and a transparent proxy:

  • A transparent proxy can modify the flow; a content filter cannot.
  • Once a transparent proxy has accepted a flow, it must proxy that flow until the flow ends. A content filter can examine part of the flow and, once it’s satisfied, return an .allow() verdict. At that point the flow’s traffic no longer comes to the filter, so it’s out of the loop.

If you decide to do filtering in your transparent proxy, it’s best not to ignore a flow that you’ve decided to drop. That produces a black hole, which can cause all sort of weird behaviour. Rather, you have to force the flow to close by calling one the close methods.

Finally, performance. All NE provider have a measurable performance impact. The fewer you add, the less that is. If you need a transparent proxy for other reasons, it’s reasonable to do your filtering there. However, you have to balance that against the performance impact of not being able to get out of the loop with an .allow() verdict.

As with many performance questions, it’s hard to offer a good answer based on theory alone. I recommend that you create a prototype, measure with realistic workloads, and then use that to set your path forward.

And if you’d like to share those results here, I’d be most interested in seeing them (-:

Share and Enjoy

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

thank you for the help!

Best practices for blocking traffic: Transparent Proxy vs Content Filter, and Multiple Network Extensions
 
 
Q