Pre-requisites
-
Amazon VPC CIDR (the /16 one) – 172.31.0.0/16 is our example
-
Ubuntu 14.04 instance launched in a public subnet with EIP attached
- EIP of the above machine – 54.63.44.120 is our example
- SSH connection to the Ubuntu instance
Setting up the server
We are going to use a distribution of OpenVPN called OpenVPN-NL (http://openvpn.fox-it.com/) because it has more secure defaults than the standard OpenVPN installation that is distributed with Ubuntu. Also, OpenVPN-NL makes use of mbed-TLS (previously PolarSSL) instead of OpenSSL because of its compactness and ease of auditability (is that even a word?). Run all following commands as the root user:

Step 1: Download and add the signature key for OpenVPN-NL
wget -qO - https://openvpn.fox-it.com/repos/fox-crypto-gpg.asc | apt-key add -
Step 2: Add OpenVPN-NL repository to the package sources list
echo deb https://openvpn.fox-it.com/repos/deb trusty main | tee -a /etc/apt/sources.list.d/openvpn-nl.list
Step 3: Install OpenVPN-NL and easy-rsa
apt-get update && apt-get -y install openvpn-nl easy-rsa
The easy-rsa package downloads utility scripts that can be used to generate certificates for the server and clients.
Step 4: Switch to the new openvpn-nl directory and create diffie-hellman parameters
pushd /etc/openvpn-nl
openssl dhparam -out dh2048.pem 2048
This step takes a while. Once it’s done, you should see a file named dh2048.pem
Step 5: Prepare to generate CA certificate and key
We could use the easy route and use the easy-rsa utility to generate our CA certificate and key. Copy easy-rsa to /etc/openvpn-nl directory. Create a directory to save the keys
cp -r /usr/share/easy-rsa /etc/openvpn-nl
mkdir /etc/openvpn-nl/easy-rsa/keys
pushd /etc/openvpn-nl/easy-rsa
Step 6: Set variables
Set the variables for the certificate in the vars file. The KEY_NAME value is important.
export KEY_COUNTRY="US"
export KEY_PROVINCE="KA"
export KEY_CITY="Metropolis"
export KEY_ORG="Krypton"
export KEY_EMAIL="kal-el@fortofsolitu.de"
export KEY_OU="SuperHeroClub"
export KEY_NAME="SecureServer"
Step 7: Export the variables
source ./vars
./clean-all #not optional
This step is NOT optional unless you know how to manually create an ‘index.txt‘ and a ‘serial‘ file under keys directory. Basically, index.txt is an empty file that contains certificate information and serial is a file with, well, a serial number.
Step 8: Generate the CA certificate
From within the easy-rsa directory type the following to build the CA file:
./build-ca
There will be prompts asking you to confirm various parameters you specified in the vars file. Just press Enter for all of them unless you want to change a particular value. This should have created a certificate ( ca.crt ) and a key file (ca.key).
Step 9: Generate the server certificate
Now that we have a CA certificate, let’s generate a server certificate for OpenVPN-NL.
Note: The trust between OpenVPN server and clients has to be bidirectional. i.e. The server needs to trust the client certificates and the clients need to trust the server certificate we are about to generate
Run the following command to generate the server certificate while within the easy-rsa directory:
./build-key-server SecureServer
Remember to replace ‘SecureServer’ with whatever you entered as the value to KEY_NAME in the vars file. Do not add a password or a company name, just ‘Enter Key your way through those questions.
This step should give us SecureServer.crt and SecureServer.key, the certificate and the key file respectively. You should also see now that the value of ‘serial’ has incremented by 1 and the index.txt file has information about the server certificate.
Copy the CA certificate, Server certificate and server certificate key to the primary OpenVPN directory.
cp /etc/openvpn-nl/easy-rsa/keys/{ca.crt,SecureServer.crt,SecureServer.key} /etc/openvpn-nl/.
Step 10: Generate a HMAC firewall
Generate a firewall that helps prevent DoS or UDP flooding attacks. This helps prevent some common attacks by authenticating the TLS connections.
openvpn-nl --genkey --secret ta.key
Step 11: Edit OpenVPN server configuration to incorporate the changes
Copy a sample server configuration file to the current directory
gunzip -c /usr/share/doc/openvpn-nl/examples/sample-config-files/server.conf.gz > /etc/openvpn-nl/server.conf
In your /etc/openvpn-nl/ directory, you should now have the following files:
- server.conf – The server configuration file
- dh2048.pem – The diffie-hellman parameter file
- ca.crt – The CA certificate
- SecureServer.crt – The server certificate
- SecureServer.key – The server certificate key file
- ta.key – The HMAC firewall file
We are now ready to configure the server. Edit the /etc/openvpn-nl/server.conf file to make changes to the parameters like so:
# Certificate settings ca ca.crt cert SecureServer.crt key SecureServer.key dh dh2048.pem # AWS VPC NETWORK / MASK push "route 172.31.0.0 255.255.0.0" # HMAC authentication tls-auth ta.key 0 # Explicit cipher specification cipher AES-256-CBC # Privilege de-escalation user nobody group nogroup # Logging log openvpn.log # ..
Un-comment the lines as necessary by removing the preceding semicolon. Replace the SecureServer.crt with the name of the files you have for server certificate. Leave the rest of the parameters as they were.
In the example, above I’m assuming my VPC’s CIDR is 172.31.0.0/16. You can be picky about the subnets you want to access by modifying the push route parameter. For example, if you have two private subnets 172.31.16.0/20 and 172.31.32.0/20, you can replace the push line with:
push "route 172.31.16.0 255.255.255.240" push "route 172.31.32.0 255.255.255.240"
Step 12: Enabling IP packet forwarding
This step allows the Linux kernel to forward network packets from the Clients on to the destination.
Edit the /etc/sysctl.conf file and un-comment the following line
net.ipv4.ip_forward=1
Load the new settings
sysctl -p
Step 13: Restart the OpenVPN-NL service
service openvpn-nl restart
Generate client certificates
Now that the server is all setup, let’s generate the client certificates. This can be done using the easy-rsa utility. In our setup, let’s assume there are two road warriors (clients on the move using laptops) that need to connect to the VPC. Let’s call them Lex and Diana; We need to generate certificates for both Lex and Diana. Let’s say Lex has a laptop and an iPhone and Diana has a laptop and an Android phone. We need to generate certificates for each device.

From within the easy-rsa directory
./build-key lexLaptop
Leave the password fields blank and press ‘enter’ all the way through the prompts. Now we are left with the following files (under the keys directory)
lexLaptop.crt lexLaptop.key lexLaptop.csr
Ignore the csr file. It’s an intermediate step (certificate signing request) and we won’t need it. Our certificate has also been signed using the CA certificate. Let’s repeat the steps for Lex’s iPhone, Diana’s Laptop and her Android phone.
./build-key lexiPhone ./build-key dianaLaptop ./build-key dianaAndroid
This should leave us with a total of four sets of certificates and key files; one each for each of Lex and Diana’s devices.
Editing the client configuration template
Copy the Client example file from the documentation directory
cp /usr/share/doc/openvpn-nl/examples/sample-config-files/client.conf /etc/openvpn-nl/easy-rsa/keys/
Edit the client.conf so that it looks like the following. Replace the values as necessary
remote <EIP_OF_OPENVPN_SERVER> 1194
ca ca.crt
cert <USER_CERT_FILE>
key <USER_KEY_FILE>
auth SHA256
cipher AES-256-CBC
Replace the <EIP_OF_OPENVPN_SERVER> with the, um.. EIP of your OpenVPN-NL server (duh). Replace the <USER_CERT_FILE> with the .crt file corresponding to the user and <USER_KEY_FILE> with the .key file corresponding to the user. Therefore, there have to be one client configuration file for each device for each user. In our case, we have
lexiphone.conf lexlaptop.conf dianaandroid.conf dianalaptop.conf
Contents of lexiphone.conf
remote 54.63.44.120 1194 ca ca.crt cert lexiPhone.crt key lexiPhone.key auth SHA256 cipher AES-256-CBC
Contents of lexlaptop.conf
remote 54.63.44.120 1194ca ca.crt cert lexLaptop.crt key lexiLaptop.key auth SHA256 cipher AES-256-CBC
Contents of dianaAndroid.conf
remote 54.63.44.120 1194 ca ca.crt cert dianaAndroid.crt key dianaAndroid.key auth SHA256 cipher AES-256-CBC
Contents of dianaLaptop.conf
remote 54.63.44.120 1194 ca ca.crt cert dianaLaptop.crt key dianaLaptop.key auth SHA256 cipher AES-256-CBC
Packaging the client files
On a standard Linux client, we can securely copy over the certificates and files and then start OpenVPN client. Do this from within the /etc/openvpn-nl directory
tar -cf lexlaptop.tgz ca.crt lexLaptop.crt lexLaptop.key ta.crt lexlaptop.conf
This compressed package can be directly uncompressed into the /etc/openvpn directory of the client and once the openvpn service is started, the connection is established
pushd /etc/openvpn-nl tar -xf lexlaptop.tgz service openvpn-nl start
Alternatively, we can create a .ovpn file that has all the information embedded in it. A ovpn file consists of the client configuration and the certificate and authentication information appended in a quasi-XML style. Here is some sample content of a .ovpn file. Let’s call this dianaAndroid.ovpn
client dev tun proto udp remote 54.169.178.87 1194 resolv-retry infinite nobind persist-key persist-tun ca [inline] cert [inline] key [inline] remote-cert-tls server tls-auth [inline] 1 auth SHA256 cipher AES-256-CBC comp-lzo verb 3 <ca> -----BEGIN CERTIFICATE----- MIIE2TCCA8GgAwIBAgIJAOkzxcy8FBPmMA0GCSqGSIb3DQEBCwUAMIGjMQswCQYD VQQGEwJJTjELMAkGA1UECBMCVE4xEDAOBgNVBAcTB0NoZW5uYWkxEDAOBgNVBAoT [..tons of text..] Ui5QLft5pC+NlN8AmFoE7MAlrVPnOpHaD4rzcE+Jk/XDhfBea1uOA+frubKCQGvs K+/mmukY4BhZKyuW4+YfeR+HgPeJc/hhpPT4U8xmE8V52e2dSilctsgqGjse -----END CERTIFICATE----- </ca> <cert> Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, ST=KA, L=Metropolis, O=Krypton, OU=SuperHeroClub, CN=Krypton CA/name=server/emailAddress=kal_el@fortofsolitu.de Validity Not Before: Feb 16 02:06:22 2016 GMT Not After : Feb 13 02:06:22 2026 GMT Subject: C=US, ST=KA, L=Metropolis, O=Krypton, OU=SuperHeroClub, CN=diana/name=server/emailAddress=kal_el@fortofsolitu.de Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:b8:09:61:59:70:09:28:d2:3a:60:ad:ef:0c:99: ee:fc:e3:c3:8c:76:4f:aa:d7:3a:ab:70:01:70:43: fc:23:37:ee:f2:a6:2e:6b:b5:b3:6b:9d:f8:9e:f2: 2b:c7:65:fb:92:6e:27:46:40:4b:38:9f:c2:50:75: 6f:ad:0b:4e:a4:e1:a6:1d:b3:78:ce:bb:30:ff:7b: bb:19:f4:9a:36:20:6f:b9:b4:c0:1e:c5:ba:6e:3c: 3b:05:09:68:97:eb:d2:ea:12:8d:9b:1c:9f:b7:1e: da:88:c6:d4:7f:dd:71:c2:6f:16:97:d9:f1:4e:01: eb:78:d9:03:57:d9:9f:a4:68:df:be:36:c7:89:7f: d7:7c:b9:d9:18:bf:18:36:0e:99:f8:a4:a8:da:a9: 99:04:4f:96:3a:7e:e2:1a:f6:5e:f0:c4:99:23:fa: 35:3c:24:b6:97:a9:1d:e7:22:f4:ca:fd:f4:b5:76: 27:cf:ae:7f:4a:7a:9e:cb:0f:11:67:73:95:b7:ce: 23:6a:4c:eb:61:d1:6c:39:bb:e9:58:2d:82:02:14: 71:44:0a:b4:54:56:0a:9b:58:32:04:c7:94:95:90: 0f:d6:7a:df:39:9b:91:0f:d8:98:74:63:c7:11:80: 56:23:ae:30:38:d4:61:2e:9d:2e:75:a6:66:4d:6b: bc:a3 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: Easy-RSA Generated Certificate X509v3 Subject Key Identifier: A6:DA:39:3E:56:00:AE:D3:67:47:F3:C1:8E:A7:94:07:E8:7D:28:6F X509v3 Authority Key Identifier: keyid:B6:7B:64:97:5D:E4:85:2A:6E:94:14:33:79:45:5C:5F:2D:16:81:3B DirName:/C=US/ST=KA/L=Metropolis/O=Krypton/OU=SuperHeroClub/CN=Krypton CA/name=server/emailAddress=kal_el@fortofsolitu.de serial:E9:33:C5:CC:BC:14:13:E6 X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Key Usage: Digital Signature X509v3 Subject Alternative Name: DNS:diana Signature Algorithm: sha256WithRSAEncryption a5:6e:2f:ea:40:59:d4:08:0c:d6:e3:c0:31:a3:53:a6:aa:e3: 03:40:6d:79:5c:0e:ff:5b:32:8b:7e:0b:9f:a3:07:71:61:8f: 8b:96:88:e6:68:9c:38:70:af:16:b2:92:34:4d:4d:d3:f4:bb: c3:e2:ab:b8:e1:01:78:6d:32:90:b0:f1:94:57:ed:5c:64:00: cf:cf:a3:48:e8:1f:ad:82:32:01:96:91:ea:51:63:64:a5:d0: 56:b1:18:45:75:e5:3a:87:6c:4b:ae:fe:4b:89:f0:88:92:53: 54:ba:2c:44:af:d8:37:06:32:7e:6c:da:ce:70:8c:3a:a2:62: bf:5a:ea:9f:ed:36:dd:fa:8f:18:15:d0:e4:01:a0:47:fb:e0: 11:2a:53:ba:e1:42:10:36:68:13:52:ce:65:7e:bc:7d:8f:11: 72:51:48:ec:e0:f2:ed:01:25:d5:85:90:7e:d8:21:05:4d:2d: 37:3e:c2:b4:fe:7e:2f:5c:a4:fe:b3:ef:bb:25:c5:91:f7:34: a9:e7:46:31:e4:a3:54:06:48:07:66:9e:a5:db:06:54:93:52: f6:6d:e1:64:aa:60:3d:38:fe:35:43:d6:e2:7d:46:4f:31:01: 29:ed:d7:3e:19:6c:b2:c1:4a:a9:91:ea:9c:ab:96:46:cf:23: 98:91:6e:e2 -----BEGIN CERTIFICATE----- MIIFMDCCBBigAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBozELMAkGA1UEBhMCSU4x CzAJBgNVBAgTAlROMRAwDgYDVQQHEwdDaGVubmFpMRAwDgYDVQQKEwdLcnlwdG9u [..tons of text..] hZB+2CEFTS03PsK0/n4vXKT+s++7JcWR9zSp50Yx5KNUBkgHZp6l2wZUk1L2beFk qmA9OP41Q9bifUZPMQEp7dc+GWyywUqpkeqcq5ZGzyOYkW7i -----END CERTIFICATE----- </cert> <key> -----BEGIN PRIVATE KEY----- MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4CWFZcAko0jpg re8Mme7848OMdk+q1zqrcAFwQ/wjN+7ypi5rtbNrnfie8ivHZfuSbidGQEs4n8JQ [..tons of text..] CrpE/OFX0qWv0OlIwOuk54NZ2SOz2Lg1S9WE25yQ/2Hq+P8VorU2n5lObNvgDDBA Ggg8vzWrCS3QUD8mSn8oQ1S7AsrHi7VuGlL8JluCETSy2QP06WBw3/9xY5sL1fWY b8MaBpCJ+XFZrqWHTxGwH2RTaw== -----END PRIVATE KEY----- </key> <tls-auth> # # 2048 bit OpenVPN static key # -----BEGIN OpenVPN Static key V1----- 7c1149d26035a1da9161553a4a7c7ec3 2e0706370d1ef87b82718ba350a47278 [..snip..] b1dcfa460b5e5e5d13c182a5c1640fbb 11e28b44af3cac731600f271abe8d90d -----END OpenVPN Static key V1----- </tls-auth>

Finally, create an IP tables rule to NAT the traffic to the subnet behind the server
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
Preparing the instance for connections
- Disable source/destination check for the instances
- Right click on the instance –> Networking –> Change source/dest. check –> Click on the blue ‘Disable’ button
- Open UDP port 1194 in the security groups to world.
Connecting the clients
Linux:
- Install OpenVPN or OpenVPN-NL
- Extract the client configuration files, certificates and keys into the /etc/openvpn directory
- Start the OpenVPN service
Debian / Ubuntu
service openvpn start
Arch Linux
# replace 'client' with the name of your # client.conf file minus the '.conf' part. systemctl start openvpn@client
Apple Macbook:
Use Tunnelblick (https://tunnelblick.net/); Import the ovpn file into the app.
- Install and launch Tunnelblick
- Select the “I have configuration files” button; then select “OpenVPN configuratin(s)” button.
- In the configurations directory that opens, copy the .ovpn file
- You should be able to launch it from the context menu from the Tunnelblick taskbar applet.
FreeBSD
- Load the tap interface kernel module
kldload if_tap
- Add the following line to /boot/loader.conf to persist the tap interface
if_tap_load="YES"
- Ensure that OpenVPN is enabled in /etc/rc.conf
openvpn_enable="YES" openvpn_if="tap"
- Copy the client configuration file, keys and certificates to /usr/local/etc/openvpn
- Start OpenVPN
/usr/local/etc/rc.d/openvpn start
Happy VPNing.