Hi All,
I am working on a macOS System Extension using Apple’s Network Extension Framework, designed to observe and log network activity at multiple layers. The system extension is currently stable and working as expected for HTTP and DNS traffic with 3 providers, getting Socket, HTTP, and DNS logs.
Current Architecture Overview
The project consists of two Xcode targets:
1. Main App Process
-
Responsible for:
- Managing system extension lifecycle (activation, configuration)
- Establishing IPC (XPC) communication with extensions
- Receiving structured logs from extensions
- Writing logs efficiently to disk using a persistent file handle
-
Uses:
OSSystemExtensionManagerNEFilterManager,NETransparentProxyManager,NEDNSProxyManagerNWPathMonitorfor network availability handling- Persistent logging mechanism (
FileHandle)
2. System Extension Process
Contains three providers, all running within a single system extension process:
a) Content Filter (NEFilterDataProvider)
-
Captures socket-level metadata
-
Extracts:
- PID via audit token
- Local/remote endpoints
- Protocol (TCP/UDP, IPv4/IPv6)
- Direction (inbound/outbound)
-
Sends structured JSON logs via shared IPC
b) Transparent Proxy (NETransparentProxyProvider)
- Intercepts TCP flows
- Creates a corresponding
NWConnectionto the destination - Captures both HTTP and HTTPS traffic, sends it to HTTPFlowLogger file which bypasses if it's not HTTP traffic.
- Uses a custom
HTTPFlowLogger:- Built using SwiftNIO library (NIO HTTP1)
- Parses up to HTTP/1.1 traffic
- Handles streaming, headers, and partial body capture (with size limits)
- Maintains per-flow state and lifecycle management
- Logs structured HTTP data via shared IPC
c) DNS Proxy (NEDNSProxyProvider)
- Intercepts UDP DNS traffic
- Forwards queries to upstream resolver (system DNS or fallback)
- Maintains shared UDP connection
- Tracks pending requests using DNS IDs
- Parses DNS packets (queries + responses) using a custom parser
- Logs structured DNS metadata via shared IPC
Shared Component: IPCConnection
-
Single bidirectional XPC channel used by all providers
-
Handles:
- App → Extension registration
- Extension → App logging
-
Uses Mach service defined in system extension entitlements
Project Structure
NetworkExtension (Project)
│
├── NetworkExtension (Target 1: Main App)
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── Info.plist
│ ├── NetworkExtension.entitlements
│ ├── Main.storyboard
│ └──ViewController.swift
│
├── SystemExtensions (Target 2: Extension Process)
│ ├── common/
│ │ ├── IPCConnection.swift
│ │ └── main.swift
│ │
│ ├── DNSProxyProvider/
│ │ ├──DNSDataParser.swift
│ │ └──DNSProxyProvider.swift (DNS Proxy)
│ │
│ ├── FilterDataProvider/
│ │ └── FilterDataProvider.swift
│ │
│ ├── TransparentProxyProvider/
│ │ ├── HTTPLogParser.swift
│ │ ├── LogDataModel.swift
│ │ └──TransparentProxyProvider.swift
│ │
│ ├── Info.plist
│ └── SystemExtensions.entitlements
│
Current Capabilities
-
Unified logging pipeline across:
- Socket-level metadata
- HTTP traffic (HTTP/1.1)
- DNS queries/responses
-
Efficient log handling using persistent file descriptors
-
Stable IPC communication between app and extensions
-
Flow-level tracking and lifecycle management
-
Selective filtering (e.g., bypass rules for specific IPs)
What's the best approach to add TLS Inspection with MITM proxy setup?
Some context and constraints:
- Existing implementation handles HTTP parsing and should remain unchanged (Swift-based).
- I’m okay with bypassing apps/sites that use certificate pinning (e.g., banking apps) and legitimate sites.
- Performance is important — I want to avoid high CPU utilization.
- I’m relatively new to TLS inspection and MITM proxy design.
Questions
- Is it a good idea to implement TLS inspection within a system extension, or does that typically introduce significant complexity and performance overhead?
- As
NETransparentProxyProvideralready intercepting HTTPS traffic, can we redirect it to a separate processing pipeline (e.g., another file/module), while keeping the existing HTTP parser(HTTPFlowLogger - HTTP only parser) intact? - What are the recommended architectural approaches for adding HTTPS parsing via MITM in a performant way?
- Are there best practices for selectively bypassing pinned or sensitive domains while still inspecting other traffic?
- Any guidance on avoiding common pitfalls (e.g., certificate handling, connection reuse, latency issues)?
I’m looking for a clean, maintainable approach to integrate HTTPS inspection into my existing system without unnecessary complexity or performance degradation.
Please let me know if any additional details from my side would help in suggesting the most appropriate approach.
Thanks in advance for your time and insights—I really appreciate it.
I’m referring to a MITM proxy in the generic sense
OK, cool. mitmproxy is super cool, but running that code in a system extension would present some challenges.
With that out of the way, let’s return to your original questions:
1- Is it a good idea to implement TLS inspection within a system extension … ?
Yes and no.
TLS inspection itself has significant drawbacks, so it’s hard to say that it’s a good idea overall. But if you’re going to implement it then using an NE transparent proxy is a reasonable way to do it. Transparent proxies do have their sharp edges, but the only alternative is to use a traditional proxy and that requires cooperation from the apps involved [1].
2- As NETransparentProxyProvider already intercepting HTTPS traffic, can we redirect it to a separate processing pipeline … while keeping the existing HTTP parser … intact?
I’m not sure I understand this question, but it seems to be about the internal architecture of your transparent proxy, and NE puts very few constraints on that. If, for example, you want process HTTP flows using one module within your product and HTTPS flows using another, that’s really your business.
3- What are the recommended architectural approaches for adding HTTPS parsing via MITM in a performant way?
Apple doesn’t have APIs for that. NE gives your transparent proxy TCP and UDP flows. Parsing the content of those flows is up to you.
Keep in mind that the world has moved on from HTTP/1. A good TLS inspection product should support HTTP/2 + TLS + TCP and HTTP/3 + QUIC + DTLS + UDP. Apple has APIs for these if you’re building a client, namely URLSession, but no APIs for implement TLS inspection. You’ll have to write or acquire your own library for implementing them, or force all connections to downgrade to HTTP/1 + TLS, which brings its own compatibility risks.
4- Are there best practices for selectively bypassing pinned or sensitive domains while still inspecting other traffic?
Again, that’s up to you. NE provides you with various mechanics, but you then have to decide how to apply those mechanics to meet your own goals.
On the subject of mechanics, there’s trade-off between features and compatibility here:
- The way to get the best compatibility and performance is not have a transparent proxy at all (-:
- Presuming that you do decide to create one, then ideally you should use the tunnel settings (
NETransparentProxyNetworkSettings) to minimise the number of flows that get routed to your proxy. - If the system sends a flow to your proxy, you should try to identify the flow in your handle-new-flow method and return false if you don’t want to handle it.
- Once you return true, there’s no going back. You become responsible for that flow.
This is a challenge because:
- The tunnel settings are very coarse-grained.
- You might find that the metadata you need to decide whether you want to handle a flow is not present when the flow arrives at your handle-new-flow method.
Combined, it means you often end up handling flows that you don’t really want to handle, and that impacts both compatibility and performance.
5- Any guidance on avoiding common pitfalls … ?
TLS inspection requires that the user install a trusted anchor, that is, a root certificate that’s trusted system wide. You can’t fully automate that. The user has to be involved. (Unless your product is installed via MDM, in which case the device manager can install the trusted anchor for you.)
Debugging NE providers is tricky. I have advice on how to approach that in Debugging a Network Extension Provider.
And lots more NE advice linked to from Network Extension Resources.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Apps that use Apple’s recommended networking APIs, URLSession and Network framework, get proxy support for free, but not all apps use those APIs.