This document explains the steps involved in creating a VPN application (iOS / macOS) from scratch with WireGuardKit.
- Xcode 13
- Doesn't work on the iOS Simulator
To keep it simple, the following are omitted:
-
Keychain:
This app passes the wg-quick config to the tunnel extension in plaintext. It's more secure to pass it through the keychain, like the official WireGuard app.
-
App Groups:
To have a shared location for writing the tunnel log, we will need an app group, which is not setup here.
- In Xcode, go to File > New > Project, select App (under either the iOS or macOS tab, depending on which platform you want), click Next.
- Set Product Name to an app name of your choice.
- Ensure Team and Organization Identifier are set correctly. Ensure that the Bundle Identifier is a unique name that has not been previously used for some other project. Select other options as applicable. Click Next.
- Select a directory to create the project. Click Create.
- In Xcode, go to the Signing & Capabilities tab for the app target
- Click on + Capability, add Network Extensions, ensure Packet Tunnel is checked. This would add the required entitlements.
- Ensure Automatically manage signing is checked. Ensure that Team is set correctly. Ensure that Provisioning Profile and Signing Certificate fields are shown without errors.
- In Xcode, go to File > New > Target, select Network Extension (under either the iOS or macOS tab, depending on which platform you want). Click Next.
- Set Product Name to an extension name of your choice, say "TunnelExtension".
- Ensure Provider Type is set to Packet Tunnel.
- Ensure Team is set correctly. Click Finish. Xcode will automatically create provisioning profiles for this target. In case Xcode prompts you to activate the "TunnelExtension" scheme, select Cancel.
- In Xcode, go to the Signing & Capabilities tab for the tunnel extension target
- Click on + Capability, add Network Extensions, ensure Packet Tunnel is checked. This would add the required entitlements.
- Ensure Automatically manage signing is checked. Ensure that Team is set correctly. Ensure that Provisioning Profile and Signing Certificate fields are shown without errors.
This enables the codebase to be used by multiple developers with different Apple Developer accounts.
-
Create an xcconfig file, say "Development.xcconfig" with the
APP_ID
andDEVELOPMENT_TEAM
fields set to appropriate values.APP_ID = <your-app-bundle-id> DEVELOPMENT_TEAM = <your-apple-developer-id>
Ideally, this file should be kept private and out of version control.
-
In Xcode's project navigator, select the project at the top. Right click on the selected item, then click on the Add files to menu item. Select the created xcconfig, click Add.
-
In Xcode's project navigator, select the project at the top. To that right of the project navigator, select the project in the Project section.
-
Under Configurations, it should show "No Configurations Set" for both Debug and Release. Expand both Debug and Release. For both Debug and Release, for the main app, set the configuration files to the "Development.xcconfig" file you created earlier.
-
Open the project.pbxproj file in a text editor.
Change all values of
DEVELOPMENT_TEAM
to inherited, like:DEVELOPMENT_TEAM = "$(inherited)";
Change all instances of the bundle id to
${APP_ID}
, like:PRODUCT_BUNDLE_IDENTIFIER = "${APP_ID}";
and, assuming your tunnel extension name is "TunnelExtension":
PRODUCT_BUNDLE_IDENTIFIER = "${APP_ID}.TunnelExtension";
- Open the App Delegate file (if you're using SwiftUI, you will need to
create an App Delegate file and set it using the
UIApplicationDelegateAdaptor
property wrapper). - Add code in the App Delegate to save the tunnel configuration and start the tunnel. (Commit: iOS)
- Open PacketTunnelProvider.swift in the tunnel extension. Make the
startTunnel
function call the supplied completion handler, so that the OS considers the tunnel to be established. (Commit: iOS)
-
Build and run the app on a test device (don't use the iOS Simulator).
The first time you run it, the OS will ask for permissions to add a VPN configuration, which you should approve.
Open Console.app and search for the text from the logs printed from the tunnel extension to make sure the tunnel is running.
The VPN badge is not shown at this stage in iOS. It's probably because we haven't setup the network settings, but I'm not sure.
- In Xcode, go to File > Add Packages.
- In the search field, paste the URL for the official wireguard-apple
repository:
https://git.zx2c4.com/wireguard-apple
. - Select 'wireguard-apple', choose the dependency rule you want, click Add Package.
This differs from how it's recommended in the wireguard-apple README
because the ${BUILD_DIR%Build/*}
used there doesn't seem to work in Xcode 13
(and maybe in Xcode 12 too).
- Write a script to build WireGuardGoBridge. (Commit)
- In Xcode, go to File > New > Target, select External Build System under the Other tab, click Next. Set Product Name as "WireGuardGoBridge". We're using a Perl script, so we set Build Tool as "/usr/bin/perl". Click Finish.
- Go to the Info tab for the "WireGuardGoBridge" target. Change Arguments to the path to the Perl script. You can use "$PROJECT_DIR" in the path, where $PROJECT_DIR is the directory that contains the Xcode project (i.e. contains the the Something.xcodeproj package).
- Go to the Build Settings tab for the "WireGuardGoBridge" target. Set
SDKROOT to
iphoneos
(if you're building for iOS), ormacosx
(if you're building for macOS). Xcode's default value seems to beiphoneos
. - Select the tunnel extension target, go to the Build Phases tab. Expand the Dependencies section, click on the "+" to add a dependency to the "WireGuardGoBridge" target.
- Select the tunnel extension target, go to the Build Phases tab. Expand the Link Binary With Libraries section, click on "+" to add "WireGuardKit"
- If you're building for iOS, go to the project's Build Settings, search for "bitcode", and set Enable Bitcode to "No".
- Copy
Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
andSources/Shared/Model/String+ArrayConversion.swift
from the wireguard-apple repository and add them to the tunnel extension. - Import WireGuardKit in "TunnelConfiguration+WgQuickConfig.swift" (Commit: iOS)
- Modify PacketTunnelProvider.swift in the tunnel extension to use WireGuardAdapter from WireGuardKit. (Commit: iOS)
- Modify AppDelegate to pass the WireGuard config to the tunnel extension. (Commit: iOS)