Post

Replies

Boosts

Views

Activity

NETransparentProxyProvider doesn't invoke handleNewFlow
I am writing a network extension (activated from a MacOS app) which should proxy HTTP connections for certain domains through a custom made proxy server. This is the code I am playing with: class AppProxyProvider: NETransparentProxyProvider { static let log = OSLog(subsystem: "com.example.myextension", category: "provider") override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) { let settings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1") let rule = NENetworkRule(destinationHost: NWHostEndpoint(hostname:"example.com", port:"443"), protocol: .TCP) settings.includedNetworkRules = [rule] self.setTunnelNetworkSettings(settings) { error in completionHandler(error) } } override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { completionHandler() } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { if let handler = completionHandler { handler(messageData) } } override func sleep(completionHandler: @escaping () -> Void) { completionHandler() } override func wake() { } override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool { os_log(.debug, log: Self.log, "HANDLE NEW FLOW") return true } } I am able to successfully invoke the startProxy method which in turn registers the custom NETransparentProxyNetworkSettings and invokes the completionHandler without errors. I can see the network extension successfully enabled in the system. But as soon as I try to make an HTTP request to the specified domain (example.com:443), the request hangs on the client and the handleNewFlow method of my network extension is never called so that I can proxy the traffic. So my question is what is the correct way to setup a NETransparentProxyProvider (or a NEAppProxyProvider)? My goal is to intercept connections to specific addresses and proxy them through a local TCP server (by wrapping them in HTTP CONNECT requests) I also tried subclassing directly from NEAppProxyProvider and registering NETunnelNetworkSettings, but in this case the completion handler of setTunnelNetworkSettings is invoked with an error: The operation couldn’t be completed. (NEAgentErrorDomain error 1.) Here's my attempt with NEAppProxyProvider: class AppProxyProvider: NEAppProxyProvider { static let log = OSLog(subsystem: "com.example.myextension", category: "provider") override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) { let settings = NETunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") self.setTunnelNetworkSettings(settings) { error in completionHandler(error) } } override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { completionHandler() } override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { if let handler = completionHandler { handler(messageData) } } override func sleep(completionHandler: @escaping () -> Void) { completionHandler() } override func wake() { } override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool { os_log(.debug, log: Self.log, "HANDLE NEW FLOW") return true } }
4
0
837
Apr ’23
NENetworkRule with destination host not working
I created a NEAppProxyProvider with the following rule: let settings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1") settings.includedNetworkRules = [ NENetworkRule(destinationHost: NWHostEndpoint(hostname: "example.com", port: "443"), protocol: .TCP) ] According to the documentation this should match all TCP port 443 traffic to hosts in the "example.com" domain. But when I test this rule with a client app, I get a "No route to host error" and the handleNewFlow method is not called: curl https://example.com -v * Trying 93.184.216.34:443... * Immediate connect fail for 93.184.216.34: No route to host * Closing connection 0 curl: (7) Couldn't connect to server If I use a network rule with a destination network then it works as expected: settings.includedNetworkRules = [ NENetworkRule(destinationNetwork: NWHostEndpoint(hostname: "93.184.216.34", port: "443"), prefix: 32, protocol: .TCP) ] Any idea what might be wrong with my domain based rule?
1
0
388
Apr ’23
Network Extension doesn't work with com.apple.security.cs.disable-library-validation entitlement
I am developing a MacOS application hosting a Network Extension (app proxy provider). I am signing with Developer ID certificate to distribute outside the AppStore and notarizing the host app with the following entitlements: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.developer.networking.networkextension</key> <array> <string>app-proxy-provider-systemextension</string> </array> <key>com.apple.developer.system-extension.install</key> <true/> <key>com.apple.security.application-groups</key> <array> <string>com.my-organization.my-group</string> </array> <key>com.apple.security.network.client</key> <true/> </dict> </plist> After notarizing the app, I am able to install and use the Network Extension. Now I have a requirement to add the following entitlements (because I need to use some third party native libraries which are signed ad-hoc): <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> <key>com.apple.security.cs.disable-library-validation</key> <true/> As soon as I add those entitlements, the application starts crashing at startup: Exception Type: EXC_CRASH (SIGKILL (Code Signature Invalid)) Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: CODESIGNING 1 Taskgated Invalid Signature Triggered by Thread: 0 Thread 0 Crashed: 0 ??? 0x11cf78ef0 _dyld_start + 0 1 ??? 0x10f62c000 ??? Thread 0 crashed with X86 Thread State (64-bit): rax: 0x0000000000000000 rbx: 0x0000000000000000 rcx: 0x0000000000000000 rdx: 0x0000000000000000 rdi: 0x0000000000000000 rsi: 0x0000000000000000 rbp: 0x0000000000000000 rsp: 0x00007ff7b08d3b98 r8: 0x0000000000000000 r9: 0x0000000000000000 r10: 0x0000000000000000 r11: 0x0000000000000000 r12: 0x0000000000000000 r13: 0x0000000000000000 r14: 0x0000000000000000 r15: 0x0000000000000000 rip: 0x000000011cf78ef0 rfl: 0x0000000000000200 cr2: 0x0000000000000000 Logical CPU: 0 Error Code: 0x00000000 Trap Number: 0 Binary Images: 0x11cf74000 - 0x11d00bfff ??? (*) <bba77709-6cad-3592-ab03-09d0f7b8610e> ??? 0x10f62c000 - 0x10f62dfff ??? (*) <4c4c44aa-5555-3144-a128-fba98974e1e0> ??? Error Formulating Crash Report: dyld_process_snapshot_get_shared_cache failed If I remove the com.apple.developer.networking.networkextension and com.apple.developer.system-extension.install, then the app starts but of course I cannot activate and use the Network Extension. So my question is whether the network extension entitlements and the disable-library-validation entitlements can be used together?
3
0
472
May ’23
Unable to communicate with Network Extension using IPC
I have a network extension (AppProxyProvider) hosted inside a Mac OS app. Both are signed with the same Developer ID. I am able to programatically install and start the extension from the hosting app: session.startTunnel() The proxy provider is successfully started and works as expected. Unfortunately I am not able to communicate with the extension from the app: session.sendProviderMessage(data) Doesn't return any error but the handleAppMessage method is never called. Also I see the following error in the system log: nesessionmanager: [com.apple.networkextension:] NESMTransparentProxySession[Primary Tunnel:My Transparent Proxy:60F50D75-194D-4FB6-A9D9-7639A561DF5E:(null)]: process 43158 is not entitled to establish IPC with plugins of type xxxxx where xxxxx is the bundle id of my hosting app. Is there some entitlement that I am missing? Could this somehow be related with the com.apple.security.application-groups entitlement and the TeamID prefix that gets prepended? I am testing this on Mac OS Ventura 13.2.1
5
0
875
Jun ’23
Capturing ipv6 traffic with AppProxyProvider on MacOS
I have implemented an AppProxyProvider (NETransparentProxyProvider) and I am able to capture traffic with it. I am also able to define network rules allowing me to exclude some traffic: let settings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1:8080") settings.includedNetworkRules = [ NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "0.0.0.0", port: "0", remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol: .TCP, direction: .outbound) ] Now the documentation states that if I want to capture localhost traffic, I need to explicitly add the following rule: NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "127.0.0.0", port: "0", remotePrefix: 8, localNetwork: nil, localPrefix: 0, protocol: .TCP, direction: .outbound) and if I want to capture ipv6 localhost address: NENetworkRule(remoteNetwork: NWHostEndpoint(hostname: "::1", port: "0", remotePrefix: 128, localNetwork: nil, localPrefix: 0, protocol: .TCP, direction: .outbound) All this works great. Now I am having trouble capturing external ipv6 traffic. For example my ISP supports ipv6 and facebook.com resolves to 2a03:2880:f128:181:face:b00c:0:25de on my machine. I am unable to write any rule allowing me to capture with the system extension such traffic. Either I get errors that the network mask cannot be greater than 32 or the traffic simply doesn't flow through the extension. Here's an example request that I would like to capture: curl https://facebook.com -kvp * Trying [2a03:2880:f128:181:face:b00c:0:25de]:443... * Connected to facebook.com (2a03:2880:f128:181:face:b00c:0:25de) port 443 (#0) * ALPN: offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Certificate (11): * (304) (IN), TLS handshake, CERT verify (15): * (304) (IN), TLS handshake, Finished (20): * (304) (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 * ALPN: server accepted h2 * Server certificate: * subject: C=US; ST=California; L=Menlo Park; O=Meta Platforms, Inc.; CN=*.facebook.com * start date: Aug 26 00:00:00 2023 GMT * expire date: Nov 24 23:59:59 2023 GMT * issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA * SSL certificate verify ok. * using HTTP/2 * h2 [:method: GET] * h2 [:scheme: https] * h2 [:authority: facebook.com] * h2 [:path: /] * h2 [user-agent: curl/8.1.2] * h2 [accept: */*] * Using Stream ID: 1 (easy handle 0x7fcb5c011e00) &gt; GET / HTTP/2 &gt; Host: facebook.com &gt; User-Agent: curl/8.1.2 &gt; Accept: */* &gt; &lt; HTTP/2 301 &lt; location: https://www.facebook.com/ &lt; strict-transport-security: max-age=15552000; preload &lt; content-type: text/html; charset="utf-8" &lt; x-fb-debug: uWVEw8FZUIXozHae5VgKvIDY5lgH/4Aph+h+nJNJpIr7jFZIFGy9LRLGCSwPudcFBdi4Mf4rLaKsNGCBxHDmrA== &lt; content-length: 0 &lt; date: Fri, 17 Nov 2023 14:14:03 GMT &lt; alt-svc: h3=":443"; ma=86400 &lt; * Connection #0 to host facebook.com left intact Can this be achieved?
3
0
592
Nov ’23