Hi!
I am building a simple per-app VPN application. I want the traffic from the certain appRules-specified application to go to the HTTP/HTTPS proxy server. I am lacking the following pieces of information:
When proxy settings are configured who receives the traffic first: a proxy server or a packet tunnel provider? Since HTTP and HTTPS protocols are on the highest level of the networking stack I expect the proxy server to receive the traffic first.
Will the packet tunnel provider receive TCP/UDP traffic from the specified applications with specified HTTP/HTTPS proxy settings?
Post
Replies
Boosts
Views
Activity
HI!
I am developing an application that should utilize ScriptingBridge.framework to interact with another process. Firstly, I created a separate test application for which I have added Apple Events entitlements via "Signing & Capabilities" section in Xcode and updated its Info.plist to have "Privacy - AppleEvents Sending Usage Description". While the test app works fine (I see an automation request popup and the process executes as expected) the main application where I want to integrate this functionality gets closed immediately after reaching the code interacting with Scripting Bridge.
On its launch, I see the following error message from tccd in Console:
Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing for accessing={TCCDProcess: identifier=<app bundleID>, ..., binary_path=<path to the app's binary>}
I had no such issues with the test app. Moreover, I should mention that the bundle I want to have with such functionality is stored in another bundle, both main and inner bundles aren't sandboxed, and the target app has Application is agent (UIElement) key set in Info.plist.
Can you suggest any ideas as to why processes behave so differently despite having pretty much the same build configurations?
Hi!
I am trying to figure out whether there are any ways to get notifications when a macOS user connects to another device via Bluetooth (specifically when a smartphone is paired with a MacBook). I know about IOBluetooth, but it seems like you have to send periodic queries for devices when using this framework, and there is no general way to receive such notifications.
I know that IOHIDManager may help to track connection with human interface devices such as keyboards, trackpads, joysticks, etc. But is there any API in IOKit/DriverKit that could help get notifications when a macOS user connects to a smartphone?
Maybe I am missing an important part of IOBluetooth, so I would appreciate any help.
Hi
I am trying to build a pie chart with SwiftUI. I have created one with a dark background, and it seems like labels that correspond to each sector (labels are of black color) are not visible. It would be better to switch these labels' foreground color to white, but I don't see any suitable method to modify this. I tried both chartYAxis and chartXAxis (they worked for BarChart), but the color didn't change. Also, I added a separate struct that conforms to LabelStyle and defines makeBody in the following way
struct WhiteLabelStyle : LabelStyle {
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.title.foregroundColor(.white)
} icon: {
configuration.icon
}
}
}
However, that also doesn't change color of labels on a chart.
Below the complete code of the view:
ZStack {
CommonConstants.defaultBackground
Chart(data, id: \.name) { name, sales in
SectorMark(angle: .value("Value", sales))
.foregroundStyle(by: .value("Product category", name))
}
.labelStyle(WhiteLabelStyle())
}
Can you suggest any ways to manipulate with a chart comprised of SectorMarks
Hi!
Trying to use notarytool for an app notarization via script on CD pipeline. Build machine has an Xcode 12.5 installed and currently it can't be upgraded. So, our team decided to use utility as standalone binary, placed in the /Applications directory.
The command used for notarization: '/Applications/notarytool submit '<path>' --keychain-profile '<profile-name>' --output-format 'json' --wait'.
Before running the command above, we saved keychain profile with notarytool store-credentials command. However, future runs of notarization script fail with a message: 'Error: No Keychain password item found for profile: '. But we definitely see it among Keychain entries and 'security find-generic-password -l ' command approves that profile has been saved.
Did anyone encounter a similar issue? Can someone suggest what how to cope with this behavior?
Thank you in advance for any ideas
Hi!
Does anybody know how to collect call stack from in a swift program. I want to be able to produce the report, similar to the one that we see in crash reports: with thread numbers and call stack for each thread in a readable text format. Can you suggest is it possible at all? If yes, how that could be achieved?
Hi!
I am trying to build a network system extension with NEPacketTunnelProvider. Actually, I want to see all outgoing packets.
So, here is my config for NETunnelProviderManager:
let protocolConfiguration = NETunnelProviderProtocol()
protocolConfiguration.providerBundleIdentifier = "bundle id"
protocolConfiguration.serverAddress = "ip address"
protocolConfiguration.providerConfiguration = [:]
manager.protocolConfiguration = protocolConfiguration
manager.isEnabled = true
manager.localizedDescription = "SamplePacketTunnel"
Currently, I want to see logs from packetFlow.readPackets. I understand that without opening the connection to the server and writing to it, I would see the absence of the Internet on my machine. However, I don't want to create the whole system for my vpn tunnel for now.
The source code inside NEPacketTunnelProvider looks like:
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
os_log(.debug, log: self.log, "Tunnel started")
let tunnelSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "same address as in protocol configuration")
let ipv4Settings = NEIPv4Settings.init(addresses: ["my local ip"], subnetMasks: ["255.255.255.0"])
var includedRoutes: [NEIPv4Route] = []
includedRoutes.append(NEIPv4Route.default())
ipv4Settings.includedRoutes = includedRoutes
tunnelSettings.ipv4Settings = ipv4Settings
setTunnelNetworkSettings(tunnelSettings) { error in
if let error = error {
os_log(.debug, log: self.log, "Error on tunnel start: %@", error.localizedDescription)
completionHandler(error)
return
}
os_log(.debug, log: self.log, "No errors on setTunnelNetworkSettings")
completionHandler(nil)
self.readTraffic()
}
}
and readTraffic:
private func readTraffic() {
os_log(.debug, log: self.log, "Started reading the traffic")
packetFlow.readPackets { dataArray, protocolsArray in
os_log(.debug, log: self.log, "Reading packets. Traffic packets count: %@", dataArray.count)
self.readTraffic()
}
}
By now I managed to see Tunnel started and Started reading the traffic logs. But there is no evidence of Reading packets. Traffic packets count messages. Moreover, in the Console I see crash report:
Thread 1 Crashed:: Dispatch queue: NEPacketTunnelFlow queue
0 libobjc.A.dylib 0x1a6b52588 objc_msgSend + 8
1 libsystem_trace.dylib 0x1a6a3cc5c _os_log_fmt_flatten_object + 132
2 libsystem_trace.dylib 0x1a6a3ac54 _os_log_impl_flatten_and_send + 1852
3 libswiftos.dylib 0x1bb8e06e8 _swift_os_log + 260
4 libswiftos.dylib 0x1bb8e760c specialized withVaList<A>(_:_:) + 452
5 libswiftos.dylib 0x1bb8e7a6c os_log(_:dso:log:_:_:) + 180
6 com.ybelikov.packet.tunnel.sample.network.extension 0x10019a23c closure #1 in PacketTunnelProvider.readTraffic() + 256 (PacketTunnelProvider.swift:50)
7 com.ybelikov.packet.tunnel.sample.network.extension 0x10019a2e4 thunk for @escaping @callee_guaranteed (@guaranteed [Data], @guaranteed [NSNumber]) -> () + 132
8 NetworkExtension 0x1b5427a04 __55-[NEPacketTunnelFlow readPacketsWithCompletionHandler:]_block_invoke + 552
9 NetworkExtension 0x1b5487134 NEVirtualInterfaceReadMultiplePackets + 1024
10 NetworkExtension 0x1b548a970 __NEVirtualInterfaceCreateReadSource_block_invoke_2 + 80
11 libdispatch.dylib 0x1a6b081b4 _dispatch_client_callout + 20
12 libdispatch.dylib 0x1a6b0b670 _dispatch_continuation_pop + 500
13 libdispatch.dylib 0x1a6b1e8e0 _dispatch_source_invoke + 1596
14 libdispatch.dylib 0x1a6b0f784 _dispatch_lane_serial_drain + 376
15 libdispatch.dylib 0x1a6b10404 _dispatch_lane_invoke + 392
16 libdispatch.dylib 0x1a6b1ac98 _dispatch_workloop_worker_thread + 648
17 libsystem_pthread.dylib 0x1a6cc8360 _pthread_wqthread + 288
18 libsystem_pthread.dylib 0x1a6cc7080 start_wqthread + 8
Could you suggest where a mistake in the tunnel settings is? I want only to read outgoing packages for now. I understand that this is not how VPN clients are supposed to work, but I am doing that only for a little experiment, so I know that in such a manner I will break the connection on my computer.
Hi everyone!
Currently, I am experimenting with NETransparentProxy Network Extension. As I understand from docs, returning false from handleNewFlow leads to routing the traffic to the original destination. Otherwise, the Network Extension is responsible for routing a flow somewhere. Currently, I simply want to play with NWConnection, read() and write() methods of NEAppProxyTCPFlow and NEAppProxyUDPFlow. My goal is to redirect traffic to the original address, but rather via using NWConnection after returning true. I have managed to do that with UDP flows, but struggling with TCP ones. These threads helped me a lot: https://developer.apple.com/forums/thread/655535?login=true, https://developer.apple.com/forums/thread/678464?page=1#671531022
However, my attempts to handle TCP flow failed. From the first thread, it is a bit unclear, what I have to do with a flow when I decide to handle it. It is stated that: "Decide if the flow is one you want your transparent proxy to handle and return true/YES in handleNewFlow." - should I save the flow object into some field of the class? I have tried, but in that case the proxy network profile changes state to "disconnected". Here is my current implementation.
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
os_log(.debug, log: self.log, "Handle new flow")
if let tcpFlow = flow as? NEAppProxyTCPFlow {
handleTCPFlow(tcpFlow)
return true
}
os_log(.debug, log: self.log, "Process id: \(processId)")
return false
}
Here is handleTCPFlow:
private func handleTCPFlow(_ flow: NEAppProxyTCPFlow) {
guard let targetEndpoint = flow.remoteEndpoint as? NWHostEndpoint else {
os_log(.debug, log: self.log, "On outboundCopierTCP. Unable to cast remote endpoint to NWHostEndpoint")
return
}
let tcpConnectionManager = TCPConnectionManager(flow: flow, endpoint: targetEndpoint)
tcpConnectionManager.startExchangingData()
}
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")
}
}
Hi! I want to try create App Proxy network system extension. However, I don't see any certain steps for creating provision profile.
Can someone describe what I have to do to create and test the extension.
How to configure provisioning profile to launch and test it on my device? Which options I should choose in Apple Developer account to generate a proper profile?
What entitlements the application and the system extension target have to include?
How to configure app group properly?
Does any ways to build and run system extension without provision profile exist?
Thank you in advance for the answer
For example I have Safari chosen as default browser on a system (e. g. macOS Big Sur) and I open Notes and type a link https://apple.com. Then Safari window pops up with new tab that loads content of web page with requested URL. My question is: how Safari knows URL it should open? Whether it is pass as argument to executable? Or there is some different mechanism?
Hi!
I have faced an issue while implementing Network Extension via Packet Tunnel Provider. When the provider tries to create UDP session using createUDPSession - https://developer.apple.com/documentation/networkextension/neprovider/1406004-createudpsession method with a server which runs on the same machine, it always got session state - https://developer.apple.com/documentation/networkextension/nwudpsession/1406901-state as failed. I have tried to host a server on a separate machine. But still the same result.
I have watched the extension logs in Console.
And found next lines:
swift
nw_socket_connect [C1:1] connectx failed (fd 5) [1: Operation not permitted]
nw_socket_connect connectx failed [1: Operation not permitted]
nw_connection_report_state_with_handler_on_nw_queue [C1] reporting state failed error Operation not permitted
Could it be problem with entitlements? Do I need some additional configurations to be able to create session with a server.
Thank you for any help!
Hi everyone!
I have recently started exploration of Network Extension capabilities in macOS. Currently I am trying to write app using network extension which should provide kind of split tunnelling. The idea is to check destinations of outgoing ip packets and when they match with some list of predefined addresses redirect these packets to another address(which is belongs to some server). If the destination doesn't match with list' elements, packet should not be redirected and simply go to the original target.
I have looked through developers' forum and watched WWDC19 session about NE. Currently I'm thinking that the best approach is to use PacketTunnelProvider to redirect packets. But I'm not sure that I'll be able to achieve needed behaviour. Is it possible to redirect packets using NE? Is it possible at all to check destination address of the outgoing packets? Also it would be great if anybody could provide a link to example project which use split tunnelling. I don't work with networks on a daily basis and will be glad to get any help from the community
Hello
I have recently started learning basics of Network Extensions. And currently investigating SimpleFirewall app source code. The app blocks incoming connections for certain port - 8888. I have tried to change firewall work logic, so it asks user to connect to certain web sites or not. I have changed startFilter() function in the FilterDataProvider class. And now it looks like
Swift
override func startFilter(completionHandler: @escaping (Error?) - Void) {
let filterRules = ["example.com"].map {address - NEFilterRule in
os_log("Initailize rule with: %@", address)
let endpoint = NWHostEndpoint(hostname: address, port: "0")
let rule = NENetworkRule(destinationHost: endpoint, protocol: .any)
return NEFilterRule(networkRule: rule, action: .filterData)
}
// Allow all flows that do not match the filter rules.
let filterSettings = NEFilterSettings(rules: filterRules, defaultAction: .allow)
apply(filterSettings) { error in
if let applyError = error {
os_log("Failed to apply filter settings: %@", applyError.localizedDescription)
}
completionHandler(error)
}
}
But this implementation doesn't works at all. Could someone explain is it enough to change filter rules to get expected app behaviour or not?