Strongswan swanctl profile for native Android IKEv2 IPsec

Android 11 seems to support IKEv2/IPsec now, so I'm attempting to build a roadwarrior swanctl profile for it. So far I'm getting as far as having an SA established, but then immediately deleted. Any advice?

The Android VPN profile has:

  • Type: IKEv2/IPsec PSK
  • Server: moon.isuldor.com
  • IPsec Identifier: strongswan at isuldor.com
  • IPsec PSK: hunter2

My vpn gateway has:

$ swanctl --version
strongSwan swanctl 5.9.0

$ cat /etc/swanctl/conf.d/android11.conf
connections {
    rw-isuldor {
        local_addrs = moon.isuldor.com
        pools = android11_pool4, android11_pool6
        fragmentation = yes
        send_cert = always
        rekey_time = 0s
        dpd_delay = 30s
        local {
            auth = pubkey
            certs = moon.pem
            id = moon.isuldor.com
        }
        remote {
            auth = psk
            id = strongswan at isuldor.com
        }
        children {
            moon {
                local_ts  = 0.0.0.0/0,::/0
                rekey_time = 0s
                dpd_action = clear
            }
        }
    }
}
secrets {
    ike-isuldor {
        id_isuldor = strongswan at isuldor.com
        secret = hunter2
    }
}
pools {
    android11_pool4 {
        addrs = 192.168.2.0/24
        dns = 1.1.1.1,1.0.0.1
    }
    android11_pool6 {
        addrs = 2607:9cf3:0:ae::6:1300/120
        dns = 2606:4700:4700::1111,2606:4700:4700::1001
    }
}

Relevant Logs from charon-systemd:

X.X.X.X is initiating an IKE_SA
IKE_SA (unnamed)[11] state change: CREATED => CONNECTING
selected proposal: IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_AES128_XCBC/MODP_3072
remote host is behind NAT
...
looking for peer configs matching Y.Y.Y.Y[moon.isuldor.com]...X.X.X.X[strongswan at isuldor.com]
selected peer config 'rw-isuldor'
authentication of 'strongswan at isuldor.com' with pre-shared key successful
...
peer requested virtual IP %any
assigning new lease to 'strongswan at isuldor.com'
assigning virtual IP 192.168.2.1 to peer 'strongswan at isuldor.com'
peer requested virtual IP %any6
assigning virtual IP <redacted> to peer 'strongswan at isuldor.com'
...
CHILD_SA moon{4} established with SPIs cba17603_i 0f8dcc81_o and TS 0.0.0.0/0 ::/0 === 192.168.2.1/32
CHILD_SA moon{4} state change: INSTALLING => INSTALLED
generating IKE_AUTH response 1 [ IDr CERT AUTH CPRP(ADDR DNS DNS) SA TSi TSr ]
splitting IKE message (2416 bytes) into 3 fragments
generating IKE_AUTH response 1 [ EF(1/3) ]
generating IKE_AUTH response 1 [ EF(2/3) ]
generating IKE_AUTH response 1 [ EF(3/3) ]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (1236 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (1236 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (84 bytes)
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
checkin IKE_SA rw-isuldor[7]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
checkin of IKE_SA successful
received packet: from X.X.X.X[38733] to Y.Y.Y.Y[4500]
waiting for data on sockets
checkout IKEv2 SA by message with SPIs ce7fea937528e3ca_i 115e7e1303dd7bc4_r
IKE_SA rw-isuldor[7] successfully checked out
received packet: from X.X.X.X[38733] to Y.Y.Y.Y[4500] (80 bytes)
parsed INFORMATIONAL request 2 [ D ]
received DELETE for IKE_SA rw-isuldor[7]
deleting IKE_SA rw-isuldor[7] between Y.Y.Y.Y[moon.isuldor.com]...X.X.X.X[strongswan at isuldor.com]
IKE_SA rw-isuldor[7] state change: ESTABLISHED => DELETING
IKE_SA deleted
generating INFORMATIONAL response 2 [ ]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733] (80 bytes)
checkin and destroy IKE_SA rw-isuldor[7]
sending packet: from Y.Y.Y.Y[4500] to X.X.X.X[38733]
IKE_SA rw-isuldor[7] state change: DELETING => DESTROYING
CHILD_SA moon{4} state change: INSTALLED => DESTROYING
deleting policy 0.0.0.0/0 === 192.168.2.1/32 out
deleting policy 192.168.2.1/32 === 0.0.0.0/0 in
deleting policy 192.168.2.1/32 === 0.0.0.0/0 fwd
deleting SAD entry with SPI cba17603
deleted SAD entry with SPI cba17603
deleting SAD entry with SPI 0f8dcc81
deleted SAD entry with SPI 0f8dcc81
lease 192.168.2.1 by 'strongswan at isuldor.com' went offline
checkin and destroy of IKE_SA successful

Update: The issue became immediately apparent once I retrieved the android logs. Basically I used adb shell to access the device, then logcat with an appropriate filter. There's probably terminal apps that can do this as well. Root was not required.

130|sargo:/ $ whoami
shell
130|sargo:/ $ logcat *:S IkeV2VpnRunner:V
--------- beginning of system
--------- beginning of main
[..] IkeV2VpnRunner: com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedException: Expected the remote/server to use PSK-based authentication but they used: 14

Conclusion: the swanctl profile should have auth=psk under the local section and an additional line assigning the pre-shared key for the server like: id_moon = moon.isuldor.com under secrets.ike-isuldor. This worked exclusively for strongswan swanctl 5.9.0, but so far I was not able to reproduce the success with an earlier version 5.7.2. I suspect the syntax may have changed in some way. But the ultimate problem was the incorrect server authentication.


Solution 1:

As the client log confirms, it expected the server to authenticate with the PSK too, not a certificate. So instead of local.auth=pubkey you'd have to configure local.auth=psk.

Note that while using a certificate on the server and a PSK on the client is supported by the IKEv2 protocol, and it does protect against other hosts knowing the PSK impersonating the server (every client has to know it and could do so), it has the same issue as IKEv2's PSK authentication has in general: the client sends the AUTH payload before verifying the server's authentication. An active attacker could use this to determine a weak PSK via dictionary or brute force attacks. A far safer approach is to use certificate authentication for the server and username/password based EAP methods (e.g. EAP-MD5 or EAP-MSCHAPv2) for the client because then the client only sends its hashed password after verifying the server certificate.