Building a custom VPN application from scratch

Hi there, I'm trying to build a MacOS VPN application from scratch. My VPN application is slightly from normal ones,

  1. It will include an authentication token and underlying process information (pid, application path etc.) in each connection made to the VPN gateway. Consider it a poor man's zerotrust implementation.
  2. NetworkExtension and PacketTunnel is a must, thus to retrieve process information via audit tokens.

However, I'm unable to find any working examples that can be built on MacOS 15.X. I tried to open an TSI case but didn't receive anything useful.

Anyone?

Answered by ohcho2KaeX in 828822022

In xcode select File -> New -> Target Choose System Network Extension, select PacketTunnelProvider.

Now I get this stub code, but don't know what to do next. I need a minimal working example.

@implementation PacketTunnelProvider
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(NSError *))completionHandler {
// maybe process the packet from there? a tcp level read / write method call?
}
- (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler: (void (^)(void))completionHandler {
// Add code here to start the process of stopping the tunnel.
completionHandler();
}
- (void)handleAppMessage:(NSData *)messageData completionHandler:(void (^)(NSData *))completionHandler {
// Add code here to handle the message.
}
- (void)sleepWithCompletionHandler:(void (^)(void))completionHandler {
// Add code here to get ready to sleep.
completionHandler();
}
- (void)wake {
// Add code here to wake up.
}
@end

In xcode select File -> New -> Target Choose System Network Extension, select PacketTunnelProvider.

Now I get this stub code, but don't know what to do next. I need a minimal working example.

@implementation PacketTunnelProvider
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(NSError *))completionHandler {
// maybe process the packet from there? a tcp level read / write method call?
}
- (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler: (void (^)(void))completionHandler {
// Add code here to start the process of stopping the tunnel.
completionHandler();
}
- (void)handleAppMessage:(NSData *)messageData completionHandler:(void (^)(NSData *))completionHandler {
// Add code here to handle the message.
}
- (void)sleepWithCompletionHandler:(void (^)(void))completionHandler {
// Add code here to get ready to sleep.
completionHandler();
}
- (void)wake {
// Add code here to wake up.
}
@end

There are three parts to this:

  • Packaging

  • Core packet tunnel functionality

  • Your specific requirements


The packaging front is relatively straightforward these days. Xcode takes care of most of the fiddly details. Two specific recommendations:


In terms of core functionality, I recently explained that to another developer on this thread. Read that, and then reply here if you have follow-up questions.


Written by ohcho2KaeX in 776258021
NetworkExtension and PacketTunnel is a must, thus to retrieve process information via audit tokens.

Hmmm, this requires careful consideration.

In general, a packet tunnel operates in destination IP mode, which means it just cares about the… well… destination IP address. It doesn’t know or care about the source application.

It’s possible to run a packet tunnel provider in source application mode, where traffic is routed to the tunnel based on the source app. This is one form of per-app VPN. In source application mode each packet has a metadata property that gives you information about the app.

However, that’s not the only way to do per-app VPN. There’s also:

  • App proxy providers

  • Transparent proxy providers

These operate in terms of flows, not packets. For example, if the app opens a TCP connection your provider gets to proxy that connection as a flow of bytes, not as TCP packets.

If you’re doing per-app stuff, these flow-based providers are usually a better option. However, if you’re working with an existing VPN server then you might not get a choice. If your server works in terms of packets, a packet tunnel provider is simplest. If your server works in terms of flows, one of the flow-based providers is simplest.

Note It’s possible to cross the streams here, but that usually involves embedding a TCP/IP stack inside your provider, using it to convert flows to packets and vice versa. That makes things substantially more complex.

Share and Enjoy

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

Per your suggestion, can you provide working minimal examples on these

  1. PacketTunnel with example code on packet processing
  2. App Proxy Provider with example code on packet processing
  3. Transparent Proxy Provider with example code on packet processing

I'm doing this for MacOS only, no need for iOS projects. I don't see anything helpful on github.

There’s no official Apple sample code that meets those criteria [1].

I’m not in a position to write sample code for you.

There are numerous open source projects for these technologies. I don’t maintain expertise on those so I can’t give you a specific recommendation.

There’s a lot of useful docs and forums posts linked to from Network Extension Resources.

I recommend that you dig into this yourself. If you have questions, search the forums for answers because I’ve helped a lot of developers with this technology over the past decade. And if you can’t find answers, feel free to post specific questions here.

Share and Enjoy

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

[1] Well, there is the very old SimpleTunnel sample but:

  • It shows the first two but not the third.

  • It’s for iOS, not macOS.

  • It’s suffered from significant bit rot, to the point where I don’t think it’s a good place for you to start.

Building a custom VPN application from scratch
 
 
Q