Setting up an OpenVPN Server on Google Compute Engine

VS
teendevs
Published in
11 min readAug 11, 2018
Photo by rawpixel on Unsplash

I recently started using OpenVPN and enjoying the various options it provides, including masking VPN traffic under TCP:443 (otherwise known as HTTPS). Personally, I like using GCE to host my VPNs, because I enjoy the power and customizability it gives.

However, there were absolutely no tutorials on setting up OpenVPN on GCE, so here you go. There are 10 steps in this tutorial, but each step may execute more than one line of code.

If you’re just trying to add more clients, go to steps 4, 9, and 10.

1. Creating the Instance

We want to use an n1-standard-1 instance for its sustainability and cost. Head over to the Compute Engine and hit “CREATE INSTANCE.”

Set it up with the options shown but don’t hit “Create” yet.

In case you can’t see the image, we’re using an n1-standard-1 instance (1 vCPU, 3.75 GB memory) with Ubuntu 16.04 LTS installed (storage space is up to you, but 10 gigs should be good enough). Since our VPN will use TCP:443, we need to allow HTTPS traffic now or later, so might as well do it right off the bat.

Press the “Management, security, disks, networking, sole tenancy” button to open up the advanced options. Click on the Networking tab and hit the edit button next to the selected Network Interface.

Click into the “External IP” dropdown and select “Create IP address.” Enter a name and hit “RESERVE.” This will attach a static IP to your instance, which is probably what you want unless you like regenerating config files every once in a while. As far as charges go, Google doesn’t charge for static IP addresses while they’re being used. The n1-standard-1 machine shouldn’t cost you more than $30 a month. Admittedly, this is a little more expensive than most VPN services, but also most VPN services don’t give you full control :)

Your Network Interfaces should look like the image. Be sure to turn on IP forwarding, as this cannot be changed after the instance has been created.

Generally, you probably want to keep your external IP secret. I don’t mind making this one public since this instance won’t exist when you’ll read this tutorial, and the IP will probably belong to someone else.

Hit “Done” and “Create.” Once the instance is ready (a solid-black “SSH” button will appear), click or double-click on said “SSH” button to open up an SSH window. The rest of the setup will be on this machine, which we will refer to as the “server.”

2. Installing OpenVPN and EasyRSA

OpenVPN is (obviously) the VPN server we’re using, and EasyRSA is a package that will allow us to set up an internal certificate authority (CA) to use.

While I don’t usually do this, for this setup it is often necessary to run an apt update to be able to install one or both of these packages. Both of them are in Ubuntu’s default repositories.

sudo apt-get update
sudo apt-get install openvpn easy-rsa

Time to configure.

3. Setting up the CA

Because OpenVPN uses TLS/SSL, it needs certificates to encrypt traffic. For this, we’ll need to issue our own trusted certificates, which we can do using the CA we’re about to set up.

Let’s start by copying the easy-rsa package’s template directory.

make-cadir ~/openvpn-ca
cd ~/openvpn-ca

There’s some values that we may want to edit, so let’s go ahead and nano vars. If you want to use vim, go ahead, but since I’m trying to keep my tutorials as simple as possible I’ll use nano.

From this point on, if there’s a portion of a command that depends on a value you choose, it will be bolded. All code that is not generated by a user will be italicized.

Scroll towards the bottom of the file (it’s not too long) and you should find the following:

export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="Fort-Funston"
export KEY_EMAIL="me@myhost.mydomain"
export KEY_OU="MyOrganizationalUnit"

Edit as many of these (or none of them) as you like. Just below these values should be export KEY_NAME="EasyRSA". To simplify our setup, we’ll change this line to read:

export KEY_NAME="server"

Save and close the file (ctrl+x, y, enter). The variables we just changed will be used for our CA’s signing process. Still inside the openvpn-ca directory, run the following command:

source vars

If you did that right, the output should read NOTE: If you run ./clean-all, I will be doing a rm -rf on /home/username/openvpn-ca/keys. This is what we want. To ensure a clean working environment, we’ll run ./clean-all .

Now, let’s build the CA.

./build-ca

All of the variables we already set should populate themselves, so just skip through by pressing enter a few times. At the end, you will have a CA ready to start signing.

We also need a server certificate and an encryption key to ensure our traffic is secure.

Let’s create our server certificate and key.

./build-key-server server

Skip through the prompts once again. We won’t enter a challenge password this time. The last two prompts require you to enter y to sign the certificate. Make sure to not skip past those!

As noted before, we also need an encryption key. For the purposes of this tutorial, we’ll generate a Diffie-Hellman key, which tends to be rather strong. Of course, with great [strength] comes great [inefficiency], so regardless of your system the following command will probably take a few minutes.

./build-dh

We’ll strengthen this with an HMAC signature, to ensure our TLS integrity verification is safer.

openvpn --genkey --secret keys/tiv.key

4. Generate a Client Certificate

Naturally, if you’re going to use a CA, your client needs to have a certificate too. While you can do this on your client machine and then have the server sign it, we’ll try to keep things simple and hosted on one machine.

If you have more than one client, you can follow this step multiple times. Just make sure to make your client names unique.

Make sure that you’re in the openvpn-ca directory and that your vars file is in sync.

cd ~/openvpn-ca
source vars

Now, we’ll build a client key as such:

./build-key client

Everything should once more be pre-populated, so skip through all except the last two prompts, which will ask you to sign by entering y.

5. Set up the OpenVPN Server

OpenVPN installs itself under the /etc/openvpn directory. To make sure everything works, we need to move some files into that folder.

cd ~/openvpn-ca/keys
sudo cp ca.crt server.crt server.key tiv.key dh2048.pem /etc/openvpn

By default, OpenVPN comes with a sample configuration. For the sake of simplicity, we’ll simplify unzip this into our config folder.

gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz | sudo tee /etc/openvpn/server.conf

Next, we’ll go ahead and edit the config file:

sudo nano /etc/openvpn/server.conf

The first step is to find the tls-auth directive. There will be a semicolon (;) next to the directive, which we’ll remove. Underneath, we’ll add a line.

tls-auth tiv.key 0 # This file is secret
key-direction 0

We also need to cipher our server, so let’s edit the cipher directives right below this section. Specifically, we’ll uncomment the AES-128-CBC line and add an auth directive.

cipher AES-128-CBC
auth SHA256

Next up are the user and group settings:

user nobody
group nogroup

Optionally, we might opt to send all traffic through the VPN. For this, find the redirect-gateway directive and uncomment it.

push "redirect-gateway def1 bypass-dhcp"

Right below, there should be a couple of dhcp-option lines. Uncomment those too.

push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

Optionally, we might want to change the port and protocol that OpenVPN operates on. The default is UDP:1194, but if your network blocks VPN connections, that will probably be one of the victims. The disguise would be to use TCP:443, which is the HTTPS port.

port 443proto tcp
;proto udp

If you didn’t use “server” as your server name, your crt files have a different name. Update them accordingly.

cert server.crt
key
server.key

Save and close the file.

6. Preparing Ubuntu

While we already set up IP forwarding and whatnot, there’s a couple other changes we need to make to enable these options.

sudo nano /etc/sysctl.conf

Look for the following line and remove the # (comment character).

net.ipv4.ip_forward=1

Save and close. To update session settings, run:

sudo sysctl -p

Next, we need to find and update our firewall (UFW) rules to masquerade clients. The first step is to find the interface that we’re running on:

ip route | grep default

The interface we want is the one that has the word “dev” in it. In our case, that looks like this:

default via 10.138.0.1 dev ens4

So, our interface is ens4. With this, we’ll update our firewall rules:

sudo nano /etc/ufw/before.rules

Above where it says Don't delete these required lines... add the following code:

# OPENVPN
# NAT Table
*nat
:POSTROUTING ACCEPT [0:0]
# OpenVPN client traffic
-A POSTROUTING -s 10.8.0.0/8 -o ens4 -j MASQUERADE
COMMIT
# OPENVPN

Save and close. Next, we need to forward packets.

sudo nano /etc/default/ufw

Find the DEFAULT_FORWARD_POLICY directive and change it from "DROP" to "ACCEPT".

DEFAULT_FORWARD_POLICY="ACCEPT"

Save and close.

7. Running OpenVPN

To start the server, run the following:

sudo systemctl start openvpn@server

To check that it started properly, run:

sudo systemctl status openvpn@server

If everything went well, you should see some output that includes Active: active (running). You might need to hit q to exit the information panel. If you’re good, link the service to the startup sequence.

sudo systemctl enable openvpn@server

8. Setting up a Client Configuration Structure

For ease of setting up client configs, we’ll first create a structure. To start, create a config folder to store the client config files.

mkdir -p ~/clients/files

The client keys will be within these configs, so let’s lock the permissions on the files directory.

chmod 700 ~/clients/files

Copy the sample configuration.

cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/clients/base.conf

Let’s edit the file:

nano ~/clients/base.conf

Find the remote directive. Replace the my-server-1 with the public external IP address that was assigned to your GCE instance. If you chose a port other than 1194, update that accordingly.

remote 35.197.111.236 443

Also update your protocol.

proto tcp

Uncomment user and group:

user nobody
group nogroup

Find the ca, cert, and key directives and comment them out, since our configs will include these automatically.

# ca ca.crt
# cert client.crt
# key client.key

Use the same cipher and auth settings as before:

cipher AES-128-CBC
auth SHA256

Somewhere, we’ll need to add key-direction. Make sure to use 1, as this is for the client now. 0 was for the server.

key-direction 1

If your current client config is (or will be) used on a Linux device, add the following:

script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

Note that if you include these in a non-Linux environment (Android and macOS included in non-Linux), your clients may act strangely.

Save and close. Next, we need to write a script to generate our client configs quickly and easily.

nano ~/clients/gen_config.sh

Inside, paste this code:

#!/bin/bashKEY_DIR=~/openvpn-ca/keys
OUTPUT_DIR=~/clients/files
BASE_CONFIG=~/clients/base.conf
cat ${BASE_CONFIG} \
<(echo -e '<ca>') \
${KEY_DIR}/ca.crt \
<(echo -e '</ca>\n<cert>') \
${KEY_DIR}/${1}.crt \
<(echo -e '</cert>\n<key>') \
${KEY_DIR}/${1}.key \
<(echo -e '</key>\n<tls-auth>') \
${KEY_DIR}/tiv.key \
<(echo -e '</tls-auth>') \
> ${OUTPUT_DIR}/${1}.ovpn

The {1} here refers to the first argument, which will be our client name. Make sure to update tiv.key according to your HMAC key name.

Allow execution of this script:

chmod 700 ~/clients/gen_config.sh

9. Generate Client Configs

The step you’ve all been waiting for is finally here. We’ll generate our client configs.

cd ~/clients
./gen_config.sh client

Check that this worked by running:

ls ~/clients/files

If it did, there should be a client.ovpn file in this directory now. We need to download this file and transfer it to our devices. To do this, click on the gear icon in the top right of the SSH session, and select “Download file.”

The fully qualified path should be something like this:

/home/username/clients/files/client.ovpn

10. Installing on Clients

For Windows, download the client app from the OpenVPN downloads. Once installed, move your client.ovpn file to C:\Program Files\OpenVPN\config. To open OpenVPN, you must run the app as administrator. Once inside, you should see and be able to connect to your VPN.

For macOS, most people suggest using Tunnelblick although any other OpenVPN client (while safe) is probably good too. Once installed, you should be able to drag any .ovpn file to the Tunnelblick icon on the menu bar to install the configuration. Use the menu bar to then connect to the VPN. If you’re asked about the down-root.so plugin, make a decision! I skipped it because I’m fine with reconnecting my connection, but it’s up to you.

On Linux, install the openvpn package using your package manager. CentOS users will need to install epel-release using yum first. Once installed, run ls /etc/openvpn. If the output does not show an update-resolve-conf file, you need to edit your .ovpn file and remove the following lines (if you had added them):

script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

If you never added those lines but found an update-resolv-conf file, add them! CentOS users will need to change the group in the .ovpn file from nogroup to nobody to comply with the operating system’s standards.

To connect, run sudo openvpn — config client.ovpn.

For iOS, install the OpenVPN Connect app. Next, open up iTunes on your computer and navigate to iPhone > Apps. Scroll to the “File Sharing” section and select “OpenVPN.” Drag your .ovpn file into the “OpenVPN Documents” panel. Opening the app on your phone should now show you a message saying a new profile is ready for import. Once imported, you should be able to connect to the VPN.

For Android, install the OpenVPN Connect app. Next, transfer your .ovpn file anywhere/anyhow on to your device (I just used Google Drive, but that might not be recommended). In the app, open the menu and click “Import.” Navigate to your .ovpn file, click on it, and hit “IMPORT” at the top right. Once completed, you should be able to connect to the VPN.

Testing the VPN

The easiest way to test your VPN would be to Google “what’s my IP address” before and after connecting to it. Your IP should change to your GCE instance’s external public IP once you’re connected to the VPN, and back to your network IP once disconnected form the VPN.

If all went well, that’s it! Your new VPN is ready for use.
Cheers!

Credits

This article is based largely off of this tutorial on the DigitalOcean community. While the setup itself is fairly similar, I thought to write a separate article because there are a few key things that are different in GCE and quite easy to get wrong if you’re doing this for the first time (I speak from experience).

If you need a second opinion, you can always turn to that article. The last section on that page also shows a way to revoke client certificates, in case you offer your VPN service to someone who goes MIA :)

Questions, comments, and concerns are always welcome. Feel free to drop me a line in the comments!

This was my first story (and the first story) on TeenDevs, so any form of support would be very greatly appreciated!

--

--