NETrasparentProxy::startProxy never gets called

Hi everyone! I'm trying to get acquainted with NETrasparentProxy abilities. What I want to achieve is to log which process initiated outbound flow. So, I have configured provisioning profiles to build and run the test app on my machine. The system extension installs and activates successfully, also I can see the network profile in Network Settings. However, I don't see a log inside override of startProxy(). I have added additional logs in main.swift of the NE and inside init() of AppProxyProvider class that inherits NETransparentProxy. Surprisingly, I do not see any logs from there. Can anyone suggest what is a possible root of the problem.

Here is the source code

class TransparentProxyService {



    private let logger = Logger()

    

    public func start() {

        logger.debug("loading proxy managers from preferences")

        NETransparentProxyManager.loadAllFromPreferences { transparentProxyManagers, error in

           // precondition(Thread.isMainThread, "TransparentProxyService::start called not from main thread")

            if let error = error {

                self.logger.error("Unable to load transparent proxy managers from prefernces. Error: \(error.localizedDescription)")

                return

            }

            self.logger.debug("successfully loaded managers")

            let proxyManagers = transparentProxyManagers ?? []



            let firstProxyManager = proxyManagers.first(where: { manager in

                guard let protocolConfiguration = manager.protocolConfiguration, let tunnelConfiguration = protocolConfiguration as? NETunnelProviderProtocol else {

                    self.logger.debug("Unable to cast protocol configuration to NETunnelProviderProtocol")

                    return false

                }

                return tunnelConfiguration.providerBundleIdentifier == "network extension bundle id"

            })

            self.logger.debug("Passed the first proxy manager search")

            let mainProxyManager = firstProxyManager ?? NETransparentProxyManager()

            self.logger.debug("Initialize config")

            self.configureWorkplaceProtocol(for: mainProxyManager)

            self.logger.debug("Config initialized. Saving to preferences")

            mainProxyManager.saveToPreferences { error in

                if let error = error {

                    self.logger.error("TransparentProxy::start. Unable to save transparent proxy manager to preferences. Error: \(error.localizedDescription)")

                    return

                }

                self.logger.debug("loading from preferences anew")

                mainProxyManager.loadFromPreferences { error in

                    if let error = error {

                        self.logger.error("TransparentProxy::start. Unable to save transparent proxy manager to preferences. Error: \(error.localizedDescription)")

                        return

                    }

                    self.logger.debug("TransparentProxy::start")

                    do {

                        try mainProxyManager.connection.startVPNTunnel()

                    } catch (let error) {

                        self.logger.error("Unable to start transparent proxy connection: \(error.localizedDescription)")

                    }

                }

            }

        }

    }
private func configureWorkplaceProtocol(for proxyManager: NETransparentProxyManager) {

        let configurationProtocol = (proxyManager.protocolConfiguration as? NETunnelProviderProtocol) ?? NETunnelProviderProtocol()

        configurationProtocol.serverAddress = "localhost"

        configurationProtocol.providerBundleIdentifier = "network extension bundle id"

        proxyManager.isEnabled = true

        proxyManager.protocolConfiguration = configurationProtocol

        proxyManager.localizedDescription = "Proxy"

    }

Can you, please, suggest what am I doing wrong here? I do not see any faults in a Console regarding the extension launch. At the same time network profile of the proxy stuck in Network Settings with "connecting" status. It seems logical, because the serverAddress of configuration is a localhost. Can I set up serverAddress like this if I only want to track outgoing Internet traffic? And the startProxy looks like this:

class AppProxyProvider: NETransparentProxyProvider {

	

    private let logger = Logger()

	let containerStorage = [1, 2, 3]

    override init() {

        logger.info("Entered init for AppProxyProvider")

        super.init()

        logger.info("Exited init for AppProxyProvider")

    }

    

    override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {

      

    	logger.info("Started proxy")

    }
  }
Answered by DTS Engineer in 724443022

I don’t see you calling the completion handler that’s passed to startProxy(options:completionHandler:). Without that the system doesn’t know that the provider has finished starting, and so it stays stuck in the ‘connecting’ state.

Share and Enjoy

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

If you’re building an NE provider system extension you should have a main.swift file that actually kicks everything off. Add a log point to the start of that and retest. Do you see those log entries? If so, there’s something wonky with how you bring up the NE provider side of things. If not, your sysex is not loading at all, and we need to explore the causes for that.

Share and Enjoy

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

Yep, I see logs from the main.swift That is what I have added

func main() -> Never {
    let logger = OSLog(subsystem: "proxy.main", category: "provider")
    os_log(.debug, log: logger, "will start system extension mode")
    autoreleasepool {
        NEProvider.startSystemExtensionMode()
    }
    os_log(.debug, log: logger, "will dispatch main")
    dispatchMain()
}
main()

So, I have managed to see the desired log about proxy start. However I don't see logs inside handleNewFlow. The method contains nothing complicated. All that I want to achieve for now is to "see" all outgoing traffic and allow it.

override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        os_log(.debug, log: self.log, "Handle new flow")
        return false
}

inside startProxy I configured NETransparentProxySettings like this, to sniff all outgoing traffic:

override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {
    	os_log(.debug, log: self.log, "Proxy started")
        let proxyNetworkSettings = NETransparentProxyNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
        let allOutgoingTCPTrafficRule = NENetworkRule(remoteNetwork: nil, remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol: .TCP, direction: .outbound)
        let allOutgoingUDPTrafficRule = NENetworkRule(remoteNetwork: nil, remotePrefix: 0, localNetwork: nil, localPrefix: 0, protocol: .UDP, direction: .outbound)
        proxyNetworkSettings.includedNetworkRules = [allOutgoingTCPTrafficRule, allOutgoingUDPTrafficRule]
        setTunnelNetworkSettings(proxyNetworkSettings) { error in
            if let error = error {
                os_log(.debug, log: self.log, "Error on saving tunnel network settings")
                return
            }
            os_log(.debug, log: self.log, "Tunnel network settings saved correctly")
        }
 }

Also, I see in the Network Settings panel that the proxy's network profile hangs in "connecting" state forever. Is there a problem with tunnelRemoteAddress? What I have to configure to be able to track outgoing flows?

Accepted Answer

I don’t see you calling the completion handler that’s passed to startProxy(options:completionHandler:). Without that the system doesn’t know that the provider has finished starting, and so it stays stuck in the ‘connecting’ state.

Share and Enjoy

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

Ah, exactly! Thank you for pointing this out. It works fine now!

NETrasparentProxy::startProxy never gets called
 
 
Q