Transparent Proxy and issues when NEHostEndpoint is hostname

Greetings,

I have encountered issues with Safari and transparent proxy (NEAppProxyTCPFlow). Safari and other apps like to invoke NEAppProxyProvider's handleNewFlow with flow's requested endpoint being hostname rather than ipv4/6 ip address.

The issue is that I have not been able to find a way how to set the actual remote IP address for the flow; it only seems that networkInterface can be used to set the actual network interface. And when this is not done, stuff starts breaking. First, lsof shows the remote address as
Code Block language
Safari 89690 username 25u IPv4 0x143206f1f81963c1 0t0 TCP 1.2.3.4:61127->*:https (CLOSED)

please note the wrong state(closed) and star instead of remote address. From syslog it seems that Safari considers the remote ip address as 0.0.0.0:443.

The problem is that this seems to break safari internally; if http2 protocol is used, it likes to use random connections to send subsequent requests (eg. the connection to youtube.com is used to send also requests for youtube videos to servers like r6---sn-2gb7sn7z.googlevideo.com). This leads to a lot of 421 HTTP responses and broken youtube etc.

Please can I somehow set the remote ip address of the flow?

Replies

What do your included and excluded Network Rules look like in your NETransparentProxyNetworkSettings configuration? I am thinking this may have some affect on what you are receiving from the system in flow.remoteEndpoint for NEAppProxyTCPFlow.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Hello,
I simply intercept all connections to specific ports (80, 443 etc.).
The issue is not that I get the hostname, that is expected, the problem is that there seems to be no way how to set the actual endpoint afterwards for the flow. How should the flow know where it connects to? AFAIK the only interaction between the internal side of transparent proxy (tcp flow) and my external one (Network.framework based connection; btw sometimes I have multiple such connections at once for single internal endpoint) is the [flow setMetadata: parameters]; which ensures that the external connection is attributed to the originating process.
IMO the endpoint should be settable, or the  [flow openWithLocalEndpoint:nullptr completionHandler:] should also accept remoteEndpoint.
Kind regards,
And just so you know: Safari used to use some kind of private getsockopt operation to get the remote address; this getsockopt could not be intercepted by a sfltfilter based kext (some kind of hack in kernel code prevented the opt to go to the getsockopt filter function). sfgetpeername callback did not apply either. This is making sflt_filter based transparent proxy not correctly work with Safari. I have even talked about this with some Sext Apple dev in the past.

For this case however this information is hopefully not really relevant, as this is not Safari specific issue.

How should the flow know where it connects to? AFAIK the only interaction between the internal side of transparent proxy (tcp flow) and my external one (Network.framework based connection; btw sometimes I have multiple such connections at once for single internal endpoint) is the [flow setMetadata: parameters]; which ensures that the external connection is attributed to the originating process.

The flow, if cast to a NEAppProxyTCPFlow for example, should have a flow.remoteEndpoint that can be used for the remote side to connect to. If you are given a hostname in handleNewFlow then this is what you will use on the remote side of the proxy. The actual flow side of this will be opened after the remote connection has gone into the ready state. The flow with be opened using an endpoint like connection.currentPath?.localEndpoint on the open(withLocalEndpoint: localEndpoint, completionHandler: completionHandler) API.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com