VPN with per-app and allowed IPs

We’re implementing VPN application using the WireGuard protocol and aiming to support both split-tunnel and per-app VPN configurations. Each mode works correctly on its own: per-app VPN functions well when configured with a full tunnel and split-tunnel works as expected when per-app is disabled. However, combining both configurations leads to issues. Specifically, the routing table is not set up properly, resulting in traffic that should not be routed through the tunnel is routed through the tunnel.

Detailed description: Through our backend, we are pushing these two plist files to the iPad one after the other:

VPN config with allowed IPs 1.1.1.1/32

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Inc//DTD PLIST 1.0//EN" http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
    <dict>
        <key>PayloadUUID</key>
        <string>3fd861df-c917-4716-97e5-f5e96452436a</string>
        <key>PayloadVersion</key>
        <integer>1</integer>
        <key>PayloadOrganization</key>
        <string>someorganization</string>
        <key>PayloadIdentifier</key>
        <string>config.11ff5059-369f-4a71-afea-d5fdbfa99c91</string>
        <key>PayloadType</key>
        <string>Configuration</string>
        <key>PayloadDisplayName</key>
        <string> test</string>
        <key>PayloadDescription</key>
        <string>(Version 13) </string>
        <key>PayloadRemovalDisallowed</key>
        <true />
        <key>PayloadContent</key>
        <array>
            <dict>
                <key>VPN</key>
                <dict>
                    <key>AuthenticationMethod</key>
                    <string>Password</string>
                    <key>ProviderType</key>
                    <string>packet-tunnel</string>
                    <key>OnDemandUserOverrideDisabled</key>
                    <integer>1</integer>
                    <key>RemoteAddress</key>
                    <string>172.17.28.1:51820</string>
                    <key>OnDemandEnabled</key>
                    <integer>1</integer>
                    <key>OnDemandRules</key>
                    <array>
                        <dict>
                            <key>Action</key>
                            <string>Connect</string>
                        </dict>
                    </array>
                    <key>ProviderBundleIdentifier</key>
                    <string>some.bundle.id.network-extension</string>
                </dict>
                <key>VPNSubType</key>
                <string>some.bundle.id</string>
                <key>VPNType</key>
                <string>VPN</string>
                <key>VPNUUID</key>
                <string>d2773557-b535-414f-968a-5447d9c02d52</string>
                <key>OnDemandMatchAppEnabled</key>
                <true />
                <key>VendorConfig</key>
                <dict>
                    <key>VPNConfig</key>
                    <string>
                                Some custom configuration here
                  </string>
                </dict>
                <key>UserDefinedName</key>
                <string>TestVPNServerrra</string>
                <key>PayloadType</key>
                <string>com.apple.vpn.managed.applayer</string>
                <key>PayloadVersion</key>
                <integer>1</integer>
                <key>PayloadIdentifier</key>
                <string>vpn.5e6b56be-a4bb-41a5-949e-4e8195a83f0f</string>
                <key>PayloadUUID</key>
                <string>9bebe6e2-dbef-4849-a1fb-3cca37221116</string>
                <key>PayloadDisplayName</key>
                <string>Vpn</string>
                <key>PayloadDescription</key>
                <string>Configures VPN settings</string>
                <key>PayloadOrganization</key>
                <string>someorganization</string>
            </dict>
        </array>
    </dict>
</plist>

Command to set up per-app with Chrome browser

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Inc//DTD PLIST 1.0//EN" http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
    <dict>
        <key>Command</key>
        <dict>
            <key>Settings</key>
            <array>
                <dict>
                    <key>Identifier</key>
                    <string>com.google.chrome.ios</string>
                    <key>Attributes</key>
                    <dict>
                        <key>VPNUUID</key>
                        <string>d2773557-b535-414f-968a-5447d9c02d52</string>
                        <key>TapToPayScreenLock</key>
                        <false />
                        <key>Removable</key>
                        <true />
                    </dict>
                    <key>Item</key>
                    <string>ApplicationAttributes</string>
                </dict>
            </array>
            <key>RequestType</key>
            <string>Settings</string>
        </dict>
        <key>CommandUUID</key>
        <string>17ce3e19-35ef-4dbc-83d9-4ca2735ac430</string>
    </dict>
</plist>

From the log we see that our VPN application set up allowed IP 1.1.1.1 via NEIPv4Settings.includedRoutes but system routing all of the Chrome browser traffic through our application.

Is this expected Apple iOS behavior, or are we misconfiguring the profiles?

Answered by DTS Engineer in 865910022
However, combining both configurations leads to issues.

Yeah, that’s not gonna work. The system won’t create two instances of your packet tunnel provider, and your single instance has to either be in one mode or the other. Note that the routingMethod property is an enum.

Share and Enjoy

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

However, combining both configurations leads to issues.

Yeah, that’s not gonna work. The system won’t create two instances of your packet tunnel provider, and your single instance has to either be in one mode or the other. Note that the routingMethod property is an enum.

Share and Enjoy

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

VPN with per-app and allowed IPs
 
 
Q