Setting up OpenVPN for Android phone (NAT & ZBFW on Cisco 1801)

I've been looking to get a decent "native" VPN setup on my android phone for a while. There doesn't seem to be native support for IPSEC VPNs terminating on Cisco routers. Long request topic for it [here] although ASA8.4(1) supposedly has support I haven't had a chance to test it yet.

For the following I used:

  • Rooted HTC Desire
  • CyanogenMod 7.0 (built in OpenVPN support :D ) http://www.cyanogenmod.com/
  • Ubuntu 10.10
  • OpenVPN 2.1.0
  • Cisco 1801 running 15.0(1)M using  zone based firewall

Setup the OpenVPN server

Since I was new to OpenVPN I followed the Ubuntu wiki found [here]. One really cool feature of OpenVPN is tls-auth you can read a bit more about it [here]. Basically if the OpenVPN server receives a packet that doesn't have a HMAC  generated by the ta.key it will just drop the packet. Makes it pretty difficult to port scan for :)

For the most part it worked perfectly but I added a couple of extra lines to the server config below. I found it useful to run the server via sudo openvpn --config server.conf --script-security 2 when I was doing the initial testing.

My server.conf file:

mode server
tls-server

local 10.1.1.10 ## ip/hostname of server
port 12345 ## My Custom port
proto udp

#bridging directive
dev tap0 ## If you need multiple tap devices, add them here
up "/etc/openvpn/up.sh br0"
down "/etc/openvpn/down.sh br0"

persist-key
persist-tun

#certificates and encryption
ca ca.crt
cert server.crt
key server.key  # This file should be kept secret
dh dh1024.pem
tls-auth ta.key 0 # This file is secret

cipher BF-CBC        # Blowfish (default)
comp-lzo

#DHCP Information
ifconfig-pool-persist ipp.txt
server-bridge 10.1.1.10 255.255.255.0 10.1.1.240 10.1.1.250
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DOMAIN m00nie.com"
max-clients 10 ## set this to the max number of clients that should be connected at a time

#log and security
user nobody
group nogroup
keepalive 10 120
status openvpn-status.log
script-security 2 ## This needed added
verb 4 ##Changed the logging level

Setup the OpenVPN client on the phone

Create the client cert

cd /etc/openvpn/easy-rsa/ ## move to the easy-rsa directory
source ./vars             ## execute the vars file
./pkitool client          ## create a cert and key named "client"

Android only seems to accept .p12 filetype certificates. You can either generate .p12 certs using this command below then copy them to your sdcard and then use the Location & Security settings > Install from SD card option to install them or just put the .crt files on a webserver and browse to them (the browser seems to accept .crt files) and it will add them to the secure store for you.

To make .p12 cert

openssl pkcs12 -export -in client.crt -inkey client.key -certfile ca.crt -out client.p12

When you copy the files to your sdcard be sure to copy the ta.key file too and note is location. If its on the sdcard its final location will be /mnt/sdcard/"somefolder"/ta.key

Now we can configure the VPN as follows. (Click for larger images)

The main config:

At the moment there is no gui setting to configure tls-auth but they did give us the Extra arguments option :D In here I added
--tls-auth /mnt/sdcard/VPN/ta.key 1 --explicit-exit-notify 2

When testing I had this error logged on the OpenVPN server:

read UDPv4 [ECONNREFUSED]: Connection refused (code=111)

I had to use the tap device rather than the tun to make the VPN connect and stop this error

Port forward & ZBFW on the router

Since at the moment we still have NAT we have to forward ports.

ip nat inside source static udp <ubuntu server IP> <server port> interface <outside address>  <some port>

so mine was
ip nat inside source static udp 10.1.1.10 12345 interface dialer 0 12345
Now we can see the static translation:

Moons-Router#show ip nat translations
Pro Inside global      Inside local       Outside local      Outside global
udp 192.168.1.1:12345 10.1.1.10:12345 ---               ---

Now the port is forwarding to the OpenVPN server but my zone based firewall config is dropping the packets.

Apr 13 18:14:04.136: %FW-6-DROP_PKT: Dropping udp session 172.16.1.1 10.1.1.10:12345 on zone-pair out-in class class-default due to  DROP action found in policy-map with ip ident 0

At the moment there is no explicit zone-pair for outside to inside so the default is deny all. Lets define one

m00nies_router#conf t
Enter configuration commands, one per line.  End with CNTL/Z.
m00nies_router(config)#access-list 199 permit udp any any eq 12345
m00nies_router(config)#class-map type inspect match-all OPENVPN
m00nies_router(config-cmap)# match access-group 199
m00nies_router(config-cmap)# exit
m00nies_router(config)#policy-map type inspect OPENVPN_POLICY
m00nies_router(config-pmap)#class type inspect OPENVPN
m00nies_router(config-pmap-c)#pass log
m00nies_router(config-pmap-c)#exit
m00nies_router(config-pmap)#exit
m00nies_router(config)#zone-pair security out-in source out-zone destination in-zone
m00nies_router(config-sec-zone-pair)# service-policy type inspect OPENVPN_POLICY
m00nies_router(config-sec-zone-pair)#^Z
m00nies_router#

First off we classify the traffic we want the allow it to pass but log it. Next we apply that policy to a zone pair. Theres more info about configuring ZBFW [here]. Below we can see the default that catches all other traffic is still drop.

m00nies_router#show policy-map type inspect OPENVPN_POLICY
Policy Map type inspect OPENVPN_POLICY
Class OPENVPN
Pass log
Class class-default
Drop

Now we can try our VPN access.
It passes the router ok

Apr 13 18:31:00.085: %FW-6-PASS_PKT: (target:class)-(out-in:OPENVPN) Passing udp pkt 172.16.1.1:52585 => 10.1.1.10:12345 with ip ident 0

And the OpenVPN server logs confirm it works ok too. Its been very stable on both 2 and 3G.

moonie :)