I'm trying to create a macOS VPN networks extension. It utilizes Wireguard via this library.
My issue is that I cannot actually seem to get the extension to connect to the VPN - I can't toggle this at all:
These are logs I get:
Bundle ID for the client app:
Bundle ID for the network extension:
Code follows; my global variables (some values hidden, but, have verified they are the correct values for the VPN I am trying to connect to):
var serverAddress: String = //
var serverPublicKey: String = //
var serverPort: String = //
private let appGroup = "6QDK6789A6.group.com.meter.MeterTunnel"
private let tunnelIdentifier = "com.meter.MeterTunnel.Tunnel"
var vpn = NetworkExtensionVPN()
var vpnStatus: VPNStatus = .disconnected
Code for setting up and connecting to the VPN (this is called from a UI event):
func connectToVPN() async {
guard let cfg = WireGuard.Configuration.make(
"Meter VPN",
appGroup: appGroup,
clientPrivateKey: clientPrivateKey,
clientAddress: clientAddress,
serverPublicKey: serverPublicKey,
serverAddress: serverAddress,
serverPort: serverPort
) else {
print("Configuration incomplete")
return
}
Task {
do {
try await vpn.reconnect(
tunnelIdentifier,
configuration: cfg,
extra: nil,
after: .seconds(2)
)
print("VPN connection started successfully")
} catch {
print("Failed to connect to VPN: \(error.localizedDescription)")
if let neError = error as? NEVPNError {
print("NEVPNError code: \(neError.code.rawValue)")
}
}
}
print("ERROR: \(String(describing: cfg.lastError))")
}
func disconnectFromVPN() {
Task {
await vpn.disconnect()
}
}
@objc private func VPNStatusDidChange(notification: Notification) {
vpnStatus = notification.vpnStatus
print("VPNStatusDidChange: \(vpnStatus)")
}
@objc private func VPNDidFail(notification: Notification) {
print("VPNStatusDidFail: \(notification.vpnError.localizedDescription)")
}
@objc private func VPNDidReinstall(notification: Notification) {
print("VPNStatusDidReinstall")
}
Status observer code set-up in the applicationDidFinishLaunching
:
NotificationCenter.default.addObserver(
self,
selector: #selector(VPNStatusDidChange(notification:)),
name: VPNNotification.didChangeStatus,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(VPNDidFail(notification:)),
name: VPNNotification.didFail,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(VPNDidReinstall(notification:)),
name: VPNNotification.didReinstall,
object: nil
)
Configuration code:
extension WireGuard {
struct Configuration {
static func make(
_ title: String,
appGroup: String,
clientPrivateKey: String,
clientAddress: String,
serverPublicKey: String,
serverAddress: String,
serverPort: String
) -> WireGuard.ProviderConfiguration? {
do {
var builder = try WireGuard.ConfigurationBuilder(clientPrivateKey)
builder.addresses = [clientAddress]
try builder.addPeer(serverPublicKey, endpoint: "\(serverAddress):\(serverPort)", allowedIPs: ["10.0.0.0/8", "224.0.0.0/4"])
builder.setKeepAlive(30, forPeer: 0)
let cfg = builder.build()
return WireGuard.ProviderConfiguration(title, appGroup: appGroup, configuration: cfg)
} catch {
print("Error creating WireGuard configuration: \(error)")
return nil
}
}
}
}
When I was developing Wireguard/OpenVPN for Apple, wireguardKit&OpenVPNAdapter was the only two available frameworks to use, then there is the tunnelKit that support both WireGuard and OpenVPN, life saver!
According to your posted configurations:
private let tunnelIdentifier = "com.meter.MeterTunnel.Tunnel"
But both the documentation and the Demo form the tunnelkit shows that:
So the tunnelIdentifier should be the bundleIdentifier of the Network Extension, may I know why you set the constant to be "com.meter.MeterTunnel.Tunnel"?
Another tips: You can use the console app to filter the logs inside the network extension:
I use process: BlockingExtension
as a filtering command to filter a network extension's logs named "BlockingExtension"(my network extension's target name), that's a more straight forward way to see what's actually happening with in the network extension instead of the app itself.