Skip to content

Latest commit

 

History

History
204 lines (154 loc) · 8.18 KB

CREATING.md

File metadata and controls

204 lines (154 loc) · 8.18 KB

Creating a VPN app with WireGuardKit

This document explains the steps involved in creating a VPN application (iOS / macOS) from scratch with WireGuardKit.

Requirements

  • Xcode 13
  • Doesn't work on the iOS Simulator

Caveats

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.

Create a basic custom-VPN Xcode project

Create an Xcode project

  1. In Xcode, go to File > New > Project, select App (under either the iOS or macOS tab, depending on which platform you want), click Next.
  2. Set Product Name to an app name of your choice.
  3. 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.
  4. Select a directory to create the project. Click Create.

Set Capabilities for the App

  1. In Xcode, go to the Signing & Capabilities tab for the app target
  2. Click on + Capability, add Network Extensions, ensure Packet Tunnel is checked. This would add the required entitlements.
  3. Ensure Automatically manage signing is checked. Ensure that Team is set correctly. Ensure that Provisioning Profile and Signing Certificate fields are shown without errors.

Add Tunnel Extension

  1. 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.
  2. Set Product Name to an extension name of your choice, say "TunnelExtension".
  3. Ensure Provider Type is set to Packet Tunnel.
  4. 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.

Set Capabilities for the Tunnel Extension

  1. In Xcode, go to the Signing & Capabilities tab for the tunnel extension target
  2. Click on + Capability, add Network Extensions, ensure Packet Tunnel is checked. This would add the required entitlements.
  3. Ensure Automatically manage signing is checked. Ensure that Team is set correctly. Ensure that Provisioning Profile and Signing Certificate fields are shown without errors.

Parametrize using xcconfig

This enables the codebase to be used by multiple developers with different Apple Developer accounts.

  1. Create an xcconfig file, say "Development.xcconfig" with the APP_ID and DEVELOPMENT_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.

  2. 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.

  3. 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.

  4. 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.

  5. 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";
    

Test tunneling

Setup a tunnel configuration

  1. 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).
  2. Add code in the App Delegate to save the tunnel configuration and start the tunnel. (Commit: iOS)

Make the tunnel extension succeed

  1. 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)

Run the app

  1. 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.

Integrate WireGuardKit

Add the WireGuardKit package

  1. In Xcode, go to File > Add Packages.
  2. In the search field, paste the URL for the official wireguard-apple repository: https://git.zx2c4.com/wireguard-apple.
  3. Select 'wireguard-apple', choose the dependency rule you want, click Add Package.

Setup a build script for WireGuardGoBridge

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).

  1. Write a script to build WireGuardGoBridge. (Commit)
  2. 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.
  3. 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).

Setup building of WireGuardGoBridge

  1. Go to the Build Settings tab for the "WireGuardGoBridge" target. Set SDKROOT to iphoneos (if you're building for iOS), or macosx (if you're building for macOS). Xcode's default value seems to be iphoneos.
  2. 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.
  3. Select the tunnel extension target, go to the Build Phases tab. Expand the Link Binary With Libraries section, click on "+" to add "WireGuardKit"

Disable bitcode

  1. If you're building for iOS, go to the project's Build Settings, search for "bitcode", and set Enable Bitcode to "No".

Use WireGuardKit in the tunnel extension code

  1. Copy Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift and Sources/Shared/Model/String+ArrayConversion.swift from the wireguard-apple repository and add them to the tunnel extension.
  2. Import WireGuardKit in "TunnelConfiguration+WgQuickConfig.swift" (Commit: iOS)
  3. Modify PacketTunnelProvider.swift in the tunnel extension to use WireGuardAdapter from WireGuardKit. (Commit: iOS)
  4. Modify AppDelegate to pass the WireGuard config to the tunnel extension. (Commit: iOS)

Copyright 2021-22 Roopesh Chander. Licensed under Creative Commons License.