Digital Adventures

ArchLinux, Ruby, Rails, OpenSource

Setting Up a VPN Server for Windows Phone 8.1

Background

I set up an openVPN server on my vhost and learned to appreciate the benefit of having a secure connection to the internet from anywhere, any time. Anywhere but Windows Phone.

The Windows Phone 8.1 update however came out packed with the much needed feature: native VPN support. Of course Microsoft only supports IPsec tunnels so that’s what needs to be set up on the server.

I found a great article on www.zeitgeist.se that explains how to set up an IPsec server on a linux box. However, I bumped into a few problems specific to Windows Phone. Those were the motivation to write this blog post for any of you that consider setting up an IPsec VPN for your Windows Phone 8.1 device.

I decided to ruthlessly copy most of the instructions from the article mentioned above and add my comments and WP-specific instructions in between to have everything in one place.

Ready… Set…

Strongswan is an OpenSource IPSec VPN server for linux and other unix based operating systems. It has a very rich documentation but I felt overwhelmed by most of it. It turns out that setting up a strongswan VPN isn’t harder than, say, openVPN.

1. Install Strongswan

The article on zeitgeist.se covers installing strongswan on Debian systems. On ArchLinux you can find strongswan in the AUR so it’s just a matter of installing it with the aur helper of your choice.

2. Create a public key infrastructure

1
cd /etc/ipsec.d

I put together a file with recurring settings:

(vars) download
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/bash

export PKI_COUNTRY=US
export PKI_ORGANIZATION=StrongSwan
export PKI_FQDN=vpn.mydomain.com

# CA Settings
export PKI_CA_KEY_SIZE=4096
export PKI_CA_EXPIRES=3600

# VPN host settings
export PKI_VPN_HOST_KEY_SIZE=2048
export PKI_VPN_HOST_EXPIRES=730

You can download it to the /etc/ipsec.d directory, edit it to your liking and then source it:

1
. ./vars

This is only for convenience and for avoiding typos.

Generate the CA Key + Certificate

1
2
3
4
5
touch private/vpn_ca.key
chmod 600 private/vpn_ca.key
ipsec pki --gen --type rsa --size $PKI_CA_KEY_SIZE \
  --outform pem \
  > private/vpn_ca.key
1
2
3
4
5
ipsec pki --self --ca --lifetime $PKI_CA_EXPIRES \
  --in private/vpn_ca.key --type rsa \
  --dn "C=${PKI_COUNTRY}, O=${PKI_ORGANIZATION}, CN=${PKI_ORGANIZATION} Root CA" \
  --outform pem \
  > cacerts/vpn_ca.crt

Generate the VPN Host Key + Certificate

1
2
3
4
5
touch private/vpn_host.key
chmod 600 private/vpn_host.key
ipsec pki --gen --type rsa --size $PKI_VPN_HOST_KEY_SIZE \
  --outform pem \
  > private/vpn_host.key
1
2
3
4
5
6
7
8
ipsec pki --pub --in private/vpn_host.key --type rsa | \
  ipsec pki --issue --lifetime $PKI_VPN_HOST_EXPIRES \
  --cacert cacerts/vpn_ca.crt \
  --cakey private/vpn_ca.key \
  --dn "C=${PKI_COUNTRY}, O=${PKI_ORGANIZATION}, CN=${PKI_FQDN}" \
  --san $PKI_FQDN \
  --flag serverAuth --flag ikeIntermediate \
  --outform pem > certs/vpn_host.crt

3. Server Configuration

StrongSwan config

/etc/ipsec.conf (ipsec.conf) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# ipsec.conf - strongSwan IPsec configuration file

config setup
  # uniqueids=never
  charondebug="cfg 2, dmn 2, ike 2, net 2"

conn %default
  keyexchange=ikev2
  ike=aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024!
  esp=aes128gcm16-ecp256,aes256gcm16-ecp384,aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024,aes128gcm16,aes256gcm16,aes128-sha256,aes128-sha1,aes256-sha384,aes256-sha256,aes256-sha1!
  dpdaction=clear
  dpddelay=300s
  rekey=no
  left=%any
  leftsubnet=0.0.0.0/0
  leftcert=vpn_host.crt
  right=%any
  rightdns=8.8.8.8,8.8.4.4
  rightsourceip=172.16.16.0/24

conn IPSec-IKEv2
  keyexchange=ikev2
  auto=add

conn IPSec-IKEv2-EAP
  also="IPSec-IKEv2"
  rightauth=eap-mschapv2
  rightsendcert=never
  eap_identity=%any
/etc/ipsec.secrets (ipsec.secrets) download
1
2
3
4
5
6
7
8
9
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.  Suitable public keys, for ipsec.conf, DNS,
# or configuration of other implementations, can be extracted conveniently
# with "ipsec showhostkey".

: RSA vpn_host.key
'Windows Phone\username' : EAP 'topsecretpassword'

Now, this part is important: You need to use 'Windows Phone\<username>' for all users connecting from a Windows Phone device. Also note the single quotes.

If strongswan was already running you need to run ipsec rereadsecrets to load the new credentials.

That was all for strongswan’s part. Next up:

IP forwarding

1
2
3
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects

To persist the settings:

/etc/sysctl.d/90-routing.conf (90-routing.conf) download
1
2
3
4
# VPN
net.ipv4.ip_forward = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

iptables/UFW

The article on zeitgeist.se describes how to set up iptables. I’m using ufw (uncomplicated firewall) on my server and it’s set up like so:

Add the following lines between the header and *filter parts in /etc/ufw/before.rules

/etc/ufw/before.rules (before.rules) download
1
2
3
4
5
6
7
8
9
# NAT (Network Address Translation) table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Allow traffic from clients to eth0
-A POSTROUTING -o eth0 ! -p esp -j SNAT --to-source 46.38.238.168

# don't delete the "COMMIT" line or the NAT table rules above won't be processed
COMMIT

Remember to replace the ip address with your VPN host’s ip as well as eth0 with your network device if necessary.

Run ufw allow 500/udp && ufw allow 4500/udp

StrongSwan Service

Since the AUR package doesn’t contain a systemd service for strongswan you need to write one yourself:

/etc/systemd/system/strongswan.service (strongswan.service) download
1
2
3
4
5
6
7
8
9
10
[Unit]
Description=strongSwan IPsec
After=network.target

[Service]
ExecStart=/usr/bin/ipsec start --nofork
StandardOutput=syslog

[Install]
WantedBy=multi-user.target

Enable and start the service:

1
2
systemctl enable strongswan
systemctl start strongswan

4. Importing the Root CA on your Windows Phone Device

One thing remains to do on the server. For your device to accept your (self signed) VPN host certificate, you need to import the root CA.

Windows Phone only supports DER formatted certificates, so you have to convert your PEM formatted certificate to DER with the following command:

1
openssl x509 -outform der -in cacerts/vpn_ca.crt -out vpn_ca.cer

No, it’s not a typo, you need the .cer extension for WP to recognize it’s a certificate.

Attach the cer file to an email and send it to an account you have access to on your device. On the device just download the attachment and import it by clicking it after it’s downloaded.

5. Set up the connection on your device

Now comes the fun part: Setting up the connection. In your settings go to VPN and add a new profile. Enter your FQDN (fully qualified domain name). This has to match the one you previously assigned to the PKI_FQDN-variable of course. Enter your username without the ‘Windows Phone\’ prefix and your password. Click on save and cross your fingers when your device connects.

Comments