How to connect your Android devices to an OpenVPN VPN

There are various OpenVPN configuration tutorials around the Internet, this post aims to fill in the gaps on how to configure the OpenVPN server, and OpenVPN for Android clients, while managing a simple firewall configured with UFW running an Arch Linux system.

Introduction

OpenVPN is a robust and highly flexible VPN daemon. OpenVPN supports SSL/TLS security, Ethernet bridging, TCP or UDP tunnel transport through proxies or NAT, support for dynamic IP addresses and DHCP, scalability to hundreds or thousands of users, and portability to most major OS platforms. Further details about OpenVPN (and advanced tips on how to to configure it) can be found in the ArchWiki OpenVPN entry.

Although OpenVPN is relatively popular among FOSS enthusiasts, the Android project does not officially support OpenVPN natively. Hopefully, the ics-openvpn (Google Play) use the Android VPN APIs to provide this functionality.

Background

This post assumes an Arch Linux system will be used to configure the OpenVPN server. It also assumes the Uncomplicated Firewall (UFW) will be used as an iptables front-end.

For information on how to configure OpenVPN on dd-wrt-enabled routers, be sure to read dd-wrt's wiki.

Installing the dependencies

The openvpn package provides the OpenVPN binary, corresponding libraries and sample configuration files. The easy-rsa package provides a set of scripts that make it easy to maintain a Public-Key Infrastructure (PKI) system, which will be required for generating the certificates for the OpenVPN server and clients. The ufw package provides ufw, the uncomplicated firewall.

Installing all these packages is as easy as typing (as root)1:

# pacman -S openvpn easy-rsa ufw
resolving dependencies...
looking for inter-conflicts...

Packages (3): easy-rsa-2.2.2-2  openvpn-2.3.6-1  ufw-0.33-3

Total Download Size:    0.49 MiB
Total Installed Size:   1.54 MiB
Net Upgrade Size:       0.00 MiB

:: Proceed with installation? [Y/n] 
:: Retrieving packages ...
 openvpn-2.3.6-1-x86_64       344.1 KiB  1110K/s 00:00 [############################]  68%
 easy-rsa-2.2.2-2-any         363.9 KiB   994K/s 00:00 [############################]  72%
 ufw-0.33-3-any               504.0 KiB   951K/s 00:01 [############################] 100%
(3/3) checking keys in keyring                         [############################] 100%
(3/3) checking package integrity                       [############################] 100%
(3/3) loading package files                             [############################] 100%
(3/3) checking for file conflicts                        [############################] 100%
(3/3) checking available disk space                    [############################] 100%
(1/3) installing ufw                                   [############################] 100%
(2/3) installing openvpn                               [############################] 100%
(3/3) installing easy-rsa                              [############################] 100%

Enabling IP forwarding

Since we want traffic from the VPN to be forwarded by the server, we have to configure IP forwarding. To do so, we have to create the file /etc/sysctl.d/ipforward.conf with the contents

net.ipv4.ip_forward = 1

And either reboot or call

# sysctl -w net.ipv4.ip_forward=1

Configuring UFW

You will probably want UFW to deny unmatched packets by default, and to allow only OpenVPN and other traffic you might be interested in (like ssh). The following commands will enable OpenVPN and http traffic, for example:

# ufw allow 1194/udp
# ufw allow ssh

We will also have to edit the file /etc/ufw/before.rules to include (after the header comments and before the *filter line) the following contents:

# nat Table rules
*nat
:POSTROUTING ACCEPT [0:0]

# Forward traffic through ppp0 - Change to match you out-interface
-A POSTROUTING -s 10.8.254.0/24 -o eth0 -j MASQUERADE

# don't delete the 'COMMIT' line or these nat table rules won't be processed
COMMIT

Last, but not least, we'll have to change the default forward policy from DROP to ACCEPT by editing the file /etc/default/ufw and setting DEFAULT_FORWARD_POLICY to ACCEPT.

Starting and enabling UFW

After UFW is properly configured, we will have to enable it, and to tell the system to start it by default by executing the following commands (only once):

# ufw enable
# systemctl start ufw
# systemctl enable ufw

Configuring the Public-Key Infrastructure

Initializing the PKI

Some tutorials recommend setting up easy-rsa in its installation directory. I'd recommend actually copying it to a user's directory and setting it up there.

First we'll copy easy-rsa to a directory we control:

$ cp -fr /usr/share/easy-rsa/ ~
$ chmod 700 ~/easy-rsa/
$ cd ~/easy-rsa

The vars file holds various configuration entries for using easy-rsa. Because of that, it may be worth editing it to use your preferred values as a default. The variables you may want to edit to change the certificates’ default fields are. Other fields are related to key sizes and may be left at their default values:

KEY_COUNTRY  # The country the Certificate Authority is in
KEY_PROVINCE # The province or state the Certificate Authority is in
KEY_CITY     # The city the Certificate Authority is in
KEY_ORG      # Your organization
KEY_EMAIL    # The Certificate Authority's administrator's email
KEY_OU       # Your organizational unit

After vars is configured, we can source it,

$ source vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on ~/easy-rsa/keys

Initilize it,

$ ./clean-all

And create the Certificate Authority,

$ ./build-ca
Generating a 2048 bit RSA private key
..........................+++
..............+++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [BR]:
State or Province Name (full name) [SP]:
Locality Name (eg, city) [SaoPaulo]:
Organization Name (eg, company) [ExampleCom]:
Organizational Unit Name (eg, section) [ACME]:
Common Name (eg, your name or your server's hostname) [ExampleCom CA]:
Name [EasyRSA]:
Email Address [[email protected]]:

And the Diffie-Hellman key exchange parameters, which can take a long time or idle-ish computers (consider using an entropy harvesting daemon, such as haveged, to speed things up here):

$ ./build-dh 
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time # rest of output omitted

After this step we have a Certificate Authority. Nice! Now on to generating the various keys and certificates we need…

Generating server keys & certificates

Generating a server key is as easy as typing ./pkitool --server $SERVER_NAME. So, if we want to generate a key for server example.com, all we have to do is:

$ ./pkitool --server example.com
Generating a 2048 bit RSA private key
....+++
writing new private key to 'example.com.key'
-----
Using configuration from ~/easy-rsa/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'BR'
stateOrProvinceName   :PRINTABLE:'SP'
localityName          :PRINTABLE:'SaoPaulo'
organizationName      :PRINTABLE:'ExampleCom'
organizationalUnitName:PRINTABLE:'ACME'
commonName            :PRINTABLE:'example.com'
name                  :PRINTABLE:'EasyRSA'
emailAddress          :IA5STRING:'[email protected]'
Certificate is to be certified until Dec 25 17:23:17 2024 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated

Generating client keys & certificates

Generating client keys and certificates is even easier than generating server keys. In this post we will create only such pair, but the process can be repeated for as many clients as you want (if you don't use OpenVPN's duplicate-cn configuration option).

$ ./pkitool example-client
Generating a 2048 bit RSA private key
.+++
writing new private key to 'example-client.key'
-----
Using configuration from ~/easy-rsa/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'BR'
stateOrProvinceName   :PRINTABLE:'SP'
localityName          :PRINTABLE:'SaoPaulo'
organizationName      :PRINTABLE:'ExampleCom'
organizationalUnitName:PRINTABLE:'ACME'
commonName            :PRINTABLE:'example-client'
name                  :PRINTABLE:'EasyRSA'
emailAddress          :IA5STRING:'[email protected]'
Certificate is to be certified until Dec 25 17:27:58 2024 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated

Enabling an “HMAC firewall”

By default, OpenVPN, may be vulnerable to DoS2 attacks and UDP port flooding. Generating a random key to be used as a shared secret helps in increasing OpenVPN's resiliency to these attacks. To generate such a key, do:

$ openvpn --genkey --secret keys/ta.key

Now we have everything we need to configure OpenVPN. Let's do it!

Configuring OpenVPN

OpenVPN comes with an extensive set of well-documented set of example configuration files. All of which can be found on /usr/share/openvpn/examples/. In that directory, the server.conf file can be found. I recommend you read the comments in that file (and try to understand what they mean), as we'll take a more succinct approach in this post.

We will assume in this post that the OpenVPN daemon will run under the nobody user and that the keys and certificates will be saved under /etc/openvpn/keys. We'll also assume the keys were generated in the same server OpenVPN will be running. This may not be the case, so you might have to replace the cp calls below to some combination of scp and ssh.

Copying the keys & certificates

The following “script”, when executed by root, will copy the files OpenVPN needs to operate as a server.

# for file in ca.crt example.com.crt example.com.key ta.key dh2048.pem
do
    cp ~/easy-rsa/keys/${file} /etc/openvpn/keys/
done
$ chown -R nobody.nogroup /etc/openvpn/keys
$ chmod 700 /etc/openvpn/keys
$ chmod 600 /etc/openvpn/keys/*

Configuring the OpenVPN server

You can either copy /usr/share/openvpn/examples/server.conf to /etc/openvpn, as mentioned above, and configure as you see fit, or configure it as below:

port 1194 # Listen on OpenVPN's default port
proto udp # Use UDP (instead of TCP)
dev tun   # Use a TUN device (instead of TAP)
ca keys/ca.crt # Path to the Certificate Authority's public certificate
cert keys/example.com.crt           # This server's certificate
key keys/example.com.key            # This server's private key
dh keys/dh2048.pem                  # The Diffie-Hellman key-exchange parameters
topology subnet                     # Subnet topology with IP addressing
server 10.8.254.1 255.255.255.0     # Server IP and netmask
ifconfig-pool-persist ipp.txt       # persistent client <-> IP "database"
push "redirect-gateway def1 bypass-dhcp" # define this as the default gateway
;duplicate-cn # uncomment if you want clients with duplicate certificates
keepalive 10 120       # ping every 10 seconds, keep alive for up to 120 seconds
tls-auth keys/ta.key 0 # Shared secret, server mode
cipher BF-CBC          # Use blowfish in CBC mode as encryption algorithm
comp-lzo               # Use LZO compression
user nobody            # Run as the "nobody" user
group nobody           # Run as the "nobody" group
persist-key            # Persist data in memory upon restart
persist-tun            # Ditto
status openvpn-status.log # Short status file location
verb 3                 # Verbosity of the logs

Configuring the Android clients

Configuring an Android client is no different than configuring a regular client.

The OpenVPN unified configuration format

The key here is to make the user's lives easier by bundling all key and certificate files into a single OpenVPN (*.ovpn) file. The key is to use the OpenVPN unified format, which allows the embedding of these files’ contents. So, if a traditional OpenVPN configuration file has entries like3

ca ca.crt
cert client.crt
key client.key
tls-auth ta.key 1

A unified OVPN file will have the following entries:

<ca>
-----BEGIN CERTIFICATE-----
MIIBszCCARygAwIBAgIE...
. . .
Lq9iNBNgWg==
-----END CERTIFICATE-----
</ca>

<cert>
-----BEGIN CERTIFICATE-----
. . .
</cert>

<key>
-----BEGIN RSA PRIVATE KEY-----
. . .
</key>

key-direction 1
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
. . .
</key>

Preparing the configuration file

A base client configuration file that will work with the server we have just configured will look like the file below (where ${SERVER_ADDRESS} is the actual server's host name or IP address):

client
dev tun
remote ${SERVER_ADDRESS}
resolv-retry infinite
nobind
persist-key
persist-tun
ca [inline]
cert [inline]
key [inline]
tls-auth [inline] 1
verb 1
keepalive 10 120
port 1194
proto udp
cipher BF-CBC
comp-lzo
remote-cert-tls server
<ca>
...
</ca>
<cert>
...
</cert>
<key>
...
</key>
<tls-auth>
...
</tls-auth>

The ellipses above will have to be substituted by the files’ contents. One easy way to do so is to use the ovpn-writer.sh script like the following:

$ cd ~/easy-rsa
$ wget https://gist.github.com/renatolfc/18e428b5a758df24455b/download -O ovpn-writer.sh
$ chmod +x ovpn-writer.sh
$ ./ovpn-writer.sh example.com ~/easy-rsa/keys/{ca.crt,example-client.crt,example-client.key,ta.key} > android-example.ovpn

This will generate the android-example.ovpn output file.

After the client files have been generated, you have to to send them to your service's user via some kind of secure channel. Selecting the channel is out of the scope of this document.

Importing the configuration file on Android

Alright! So the user has the ovpn files sitting right in his/her SD card. After OpenVPN for Android is installed, and started, one will be presented by the main OpenVPN for Android interface. Tapping the folder icon will bring a file selection dialog, which, after having selected the file, will present a screen similar to the image below.

Home interface of Android OpenVPN

Selecting the android-example profile will bring a warning dialog as depicted below. One has to accept that OpenVPN will be able to intercept all network traffic to continue.

Android warning OpenVPN will be able to see all network traffic

After a successful connection, the key icon, highlighted in the screenshot below will be present in Android's notification bar. Tapping that icon will display statistics about the connection, as shown below:

OpenVPN for Android after a successful connection

And that's it! By now you should have a working OpenVPN server and a client running on Android.


  1. As you may be aware, this assumes the local package database is updated, and will only download and install the new packages. You might need to combine the -y and the -u options. (Which will sync the package database and update all packages.) Tweak the command line as needed.

  2. Some journalists say this is a form of hacking. As @SwiftOnSecurity would say: Please stop calling DDoS attacks, “hacker attacks.” That's like calling taking a dump, “biological terrorism.”

  3. Taken from OpenVPN for Android FAQ.

Avatar
Renato Luiz de Freitas Cunha
Principal Research Software Engineer

My research interests include reinforcement learning and distributed systems