OpenVPN on CoreOS/EC2

Many public wifi networks are unencrypted to allow password-less access, which makes it easy for someone to sniff your wifi traffic or mount a man-in-the-middle attack. Encrypting the connection via a VPN helps protect your data when it’s intercepted.

Solution Summary

Let’s build a vpn “tunnel” through the internet between my client (ie, laptop/phone) and another secure machine (ie, the server). We’ll collectively call these machines the tunnel endpoints.

All data entering the tunnel is encrypted as it enters an endpoint, and that data is decrypted only as it leaves the tunnel via the other endpoint. All hops along the tunnel and all observers watching the tunnel’s traffic will not be able to decrypt the data.

The Server Endpoint

To protect your wifi traffic, the server needs to be on the internet side of the wifi AP. In other words, the wifi network should be between the two endpoints, so that traffic traversing the wifi network will be encrypted in our VPN tunnel. When connected to the VPN, all internet-bound traffic from the client will pass through this endpoint, so it must be secure, highly available, and provided with sufficient bandwidth. Example diagram below:

client <–encrypted tunnel–> server <–unencrypted internet–> arstechnica.com

AWS micro instances provide cheap real estate (a reserved instance costs $3-4 a month) and decent network bandwidth to ensure the VPN won’t impose a noticeable connection bottleneck for basic web activity. Even youtube and rdio work just fine.

I initially configured an OpenVPN server running on Ubuntu, but have recently switched to an easy-to-deploy docker container running on a CoreOS AMI. Core’s containerized environment and automated patching are good characteristics for an endpoint that must remain secure despite being exposed to the open internet.

Software Ingredients

Keys, Certificates, and CAs

The easy way to get OpenVPN running is to use symmetric crypto, which requires all endpoints to share the same secret key. This makes key loss catastrophic, however - a compromised phone could compromise the other clients. Fortunately, the docker container provides asymmetric crypto via public key encryption, which is more complex to configure but provides a unique private key to the server and to each client.

Because a client never knows the server’s secret key, a compromised client key only compromises that client. As an added perk, access can be revoked on a per-client basis if needed.

As an overview, here are typical components for a two-client setup under a public key setup (see openvpn docs):

Since the Certificate Authority issues the server and client certificates, it is the ultimate source of trust here. In our small example, this doesn’t mean much, but in a larger VPN provider, this means you only need to trust one entity (the CA) to know that you’re connecting to the right endpoint, since you can verify that the endpoint’s cert was issued by that CA. This is a deep subject in security, but suffice it to say that CA trust chains are a topic of interest to security experts.

Implementation

###Step 1: Deploy OpenVPN Server Software
There are OpenVPN configuration guides elsewhere online. I have previously used Ubuntu 12.04 Server Guide, but fortunately docker makes this a non-issue.

Encapsulating the server in a container makes sense for security and portability reasons; the server doesn’t need access to the rest of the host machine, and a portable container means I can destroy my server instance and boot up another one with ease.

@kylemanna has published a fork of @jpetazzo’s OpenVPN docker container configured for PKI. It is simple to deploy on the server machine. These instructions should be implemented on the server machine. If you’re using a CoreOS host like I am, I’ve published a unit file and instructions at github.com/mypetyak/unit-openvpn.

###Step 2: Retrieve Server Keys and Certs Fortunately, too, the container comes with instructions to extract a single *.ovpn text file with complete client configuration, server certificate, client certificate, and client private key.

This *.ovpn file contains all information needed to establish a trusted connection with the VPN server, so it should be guarded. A secure way to easily transfer this file to the client is to pull it via scp. Assuming you’re running OpenVPN on an AWS EC2 instance:

On the (OS X) client machine:

scp -i <your_ec2_ssh_credentials.pem> username@server_ip:/<path_to_ovpn_file> client.ovpn

###Step 3: Install Client OpenVPN-compliant client software is available for many platforms. On OS X, I use Tunnelblick.

The *.ovpn file format produced by the server is readily imported into most clients. With Tunnelblick, just double click the ovpn file. With OpenVPN Connect on iOS, follow these instructions

Notes

21:53:37.091286 IP 10.1.242.230.54450 > ec2-XXX-XXX-XXX-XXX.compute-1.amazonaws.com.openvpn: UDP, length 93  
0x0000:  4500 0079 ab0c 0000 4011 a9c5 0a01 f2e6  E..y....@.......  
0x0010:  36a4 f216 d4b2 04aa 0065 a2c2 31c8 1454  6........e..1..T  
0x0020:  d362 e0d9 cf27 f5b6 3d04 e2c7 a168 eef5  .b...'..=....h..  
0x0030:  df9a fad3 246b 9c58 5c08 23fb 5b3d 0b3a  ....$k.X\.#.[=.:  
0x0040:  3ce1 05eb 7bc5 2f6a db5b e252 21d8 f2ca  <...{./j.[.R!...  
0x0050:  8975 9b84 8f7c 2352 60fa a757 790a ad27  .u...|#R`..Wy..'  
0x0060:  94f2 7797 e6ad 90e9 c11a 7300 cb86 a978  ..w.......s....x  
0x0070:  0a26 a222 a15d 1dd5 0d                   .&.".]...