Create Your Own VPN

Introduction

In this article, VPN (Virtual Private Network) refers to an encrypted point-to-point link that reroutes all of your Internet traffic, making it appear to come from a computer other than your own. Why would you want to do this? There are several reasons, the main one being to empower you "to choose" your ISP (Internet Service Provider).

Not that long ago, there were an estimated 7,000 ISPs in the United States and specialized review sites that helped consumers choose between them. If you wanted to chose an ISP that respected your privacy, the free market would effectively let you do that. Today that choice is gone.

Now the best you can do is to choose a "remote" ISP that meets your needs and tunnel your traffic to it through a VPN.

This article offers an opinionated, do-almost-everything-for-you script to set up a personal VPN. It assumes you're using macOS, DigitalOcean and IPv4.

Threat Model

This section enumerates some of the threats from which a VPN can protect you. They are only theoretical possibilities and may not actually be occurring.

Your ISP may be Altering Your Traffic

In 2007, Comcast [the second largest Internet Service Provider (ISP) in the United States] installed new software or equipment on its networks that began selectively interfering with ... TCP/IP connections.

Now that we lost network neutrality in 2018 I expect a lot more of this to happen.

Your Online Behavior may be Sold

In 2017, the United States' House of Representatives passed a resolution of disapproval to overturn the Broadband Consumer Privacy Proposal privacy law by the Federal Communications Commission (FCC). Internet Service Providers can now sell your browsing habits, app usage history, location data and Social Security numbers directly to marketers, financial firms and other companies that mine personal data without your consent.

Your DNS may be Spoofed

The Domain Name System (DNS) is vulnerable:

... vulnerabilities in the DNS were discovered that allow an attacker to hijack this process of looking some one up or looking a site up on the Internet using their name. The purpose of the attack is to take control of the session to, for example, send the user to the hijacker's own deceptive web site for account and password collection.

These vulnerabilities have increased interest in introducing a technology called DNS Security Extensions (DNSSEC) to secure this part of the Internet's infrastructure.

DNSSEC was supposed to save us, however:

The short answer is: pretty much everyone uses normal DNS, because the many show-stopping problems with DNSSEC includes the insane design decision not to protect the "last mile" between the stub resolver on your own machine and the "DNS server" (technically: recursive cache) that DHCP configures.

If you're using Google's DNS, it will (pretty much pointlessly) validate DNSSEC records for you --- but the link between your computer and Google's DNS servers are completely unprotected (any attacker could simply trick your browser into believing there was no such thing as DNSSEC).

This doesn't much matter because only a tiny, tiny fraction of all DNS records are DNSSEC-signed. The modal experience for companies that do take the trouble to sign their DNS records is "taken offline completely by DNSSEC configuration mistakes". There is virtually no upside to participating.

A VPN doesn't solve the problem (try DNS-over-TLS for that) but it lessens the risk.

Your Identity/Behavior may be Correlated with Your Location

If any of your Internet traffic can identify who you are, and your physical location can be inferred by your IP address and/or the owner of the wifi hotspot you're using, then the two can be correlated.

Why WireGuard?

There are three principle choices for VPN software: IPsec, OpenVPN, and the newcomer WireGuard.

Classified documents published by Der Spiegel indicate the NSA is passively decrypting IPsec connections at significant scale, so that's out.

OpenVPN isn't as vulnerable to the weakness that's conjectured to be exploited by the NSA but it relies on OpenSSL (which doesn't have a good track record), has an enormous attack surface, and is slow.

WireGuard is a modern VPN with a relatively tiny attack surface that uses state-of-the-art cryptography and is fast. Its website reads, "It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry", a credible claim. It also comes with the warning, "WireGuard is not yet complete. You should not rely on this code. It has not undergone proper degrees of security auditing and the protocol is still subject to change" but I think it's already good enough for a personal VPN.

It's being considered for inclusion in the Linux kernel and here's what Linus Torvalds has to say about it:

Can I just once again state my love for it and hope it gets merged soon? Maybe the code isn't perfect, but I've skimmed it, and compared to the horrors that are OpenVPN and IPSec, it's a work of art.

Linus

Attack Surface

The fewer lines of code, the better.

Software Lines of Code
WireGuard4,000
IPSec (XFRM + StrongSwan)400,000
OpenVPN (including OpenSSL)600,000

Monitoring Tool

I wrote a simple monitoring tool that displays a colored circle in the menu bar to indicate whether or not the VPN is working. It functions by sending a request to Ipify once every hour to get your external IP address and to make sure it's the right one.

DigitalOcean

Why DigitalOcean?

You can use any good cloud computing provider with a clear privacy policy to create your personal VPN. I chose one for the script for simplicity. Here is DigitalOcean's privacy policy:

March 8, 2017

Official statement:

We have not ever, nor will we ever, sell customer information to anyone, anywhere, anytime.

Nor has this ever been considered or brought up by anyone at DigitalOcean. Ever.

Thank you,

Moisey

Implementation

Create the Droplet

  1. Create a droplet.
  2. Choose Ubuntu 18.04 x64.
  3. Choose the least expensive size.
  4. Choose the datacenter region closest to you.
  5. Select the User data checkbox.

Enter the following where it reads Enter user data here... to prevent a Man-in-the-Middle attack:

#!/bin/sh
ssh-keygen -f /etc/ssh/ssh_host_ed25519_key.pub -l -v >> /etc/issue  

Add your SSH key, give it a hostname like vpn if you like, and click Create.

Open Terminal and set the ipv4 variable to the correct address for the droplet, e.g.,

$ export ipv4=192.0.2.1

Show the Fingerprint

This will come in useful later.

  1. Click the ellipsis menu to the right of the droplet name.
  2. Click Access console.
  3. Keep the console window open so that you can refer to it later.

macOS

First install Homebrew to make it easier to install various tools.

Create a directory on your Mac for this project.

$ mkdir ~/Documents/vpn
$ cd ~/Documents/vpn

Rather than explain how you to do each one of the steps, I've put them all into a shell script for simplicity. Please scrutinize the script, make sure you understand what it's doing, and feel free to make changes to it or to perform each step manually.

Create a file named setup.sh with the following contents:

#!/bin/sh

if [ -z $ipv4 ]; then  
  echo Please set the ipv4 variable like this first: export ipv4=192.0.2.1
  exit 1
fi

echo Using IP address $ipv4.

set -o errexit  
set -o verbose

ListenPort=51820

# Check the VPN status every hour.
StartInterval=3600

# Install various tools we'll need (Git, Node.js, WireGuard, and Yarn).
brew install git node wireguard-tools yarn

# Generate cryptographic keys.
PresharedKey=$(wg genpsk)  
clientPrivateKey=$(wg genkey)  
clientPublicKey=$(echo $clientPrivateKey | wg pubkey)  
serverPrivateKey=$(wg genkey)  
serverPublicKey=$(echo $serverPrivateKey | wg pubkey)

# Create the client configuration file.
cat > utun.conf << EOF  
[Interface]
Address = 10.0.0.2/32  
DNS = 1.1.1.1  
PrivateKey = $clientPrivateKey

[Peer]
AllowedIPs = 0.0.0.0/0  
Endpoint = $ipv4:$ListenPort  
PresharedKey = $PresharedKey  
PublicKey = $serverPublicKey  
EOF

chmod go= utun.conf

# Confirm that the key fingerprint matches the one in the DigitalOcean console
# before proceeding.

set +o verbose

ssh root@$ipv4 -o HostKeyAlgorithms=ssh-ed25519 -o VisualHostKey=yes << EOSSH  
set -o verbose

# Configure the droplet.

# Install WireGuard.
# https://www.wireguard.com/install/
add-apt-repository ppa:wireguard/wireguard  
apt-get update  
apt-get install --yes wireguard

# Set up the firewall.

sed --in-place 's/#net\/ipv4\/ip_forward=1/net\/ipv4\/ip_forward=1/' /etc/ufw/sysctl.conf  
sysctl --write net.ipv4.ip_forward=1

ufw allow ssh  
ufw allow $ListenPort/udp  
ufw --force enable

cd /etc/wireguard/

# Create the configuration file.
cat > wg0.conf << "EOF"  
[Interface]
Address = 10.0.0.1/32  
ListenPort = $ListenPort  
PostUp = iptables --append FORWARD --in-interface wg0 --jump ACCEPT  
PostDown = iptables --delete FORWARD --in-interface wg0 --jump ACCEPT  
PostUp = iptables --table nat --append POSTROUTING --out-interface eth0 --jump MASQUERADE  
PostDown = iptables --table nat --delete POSTROUTING --out-interface eth0 --jump MASQUERADE  
PrivateKey = $serverPrivateKey

[Peer]
AllowedIPs = 10.0.0.0/24  
PresharedKey = $PresharedKey  
PublicKey = $clientPublicKey  
EOF

chmod go= wg0.conf

# Tell WireGuard to start on boot.
systemctl enable wg-quick@wg0

# Bring up the VPN interface.
wg-quick up wg0  
EOSSH

set -o verbose

# Bring up the VPN connection on macOS.  You will need to type your password to
# allow this.
sudo wg-quick up ./utun.conf

# Install Homebrew Cask so that it's easy to install AnyBar.
brew tap caskroom/cask

# Install and start AnyBar.
brew cask install anybar  
/Applications/AnyBar.app/Contents/MacOS/AnyBar &

# Download my simple monitoring tool.
git clone https://gitlab.com/NodeGuy/vpn-monitor  
(cd vpn-monitor; yarn install)

# Tell macOS to launch the monitoring tool at login.
cat > ~/Library/LaunchAgents/vpn.status.plist << EOF  
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">  
<plist version="1.0">  
  <dict>
    <key>Label</key>
    <string>vpn.status</string>

    <key>ProcessType</key>
    <integer>Background</integer>

    <key>ProgramArguments</key>
    <array>
      <string>$(which node)</string>
      <string>$(pwd)/vpn-monitor/lib/index</string>
      <string>--expected</string>
      <string>$ipv4</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>StartInterval</key>
    <integer>$StartInterval</integer>
  </dict>
</plist>  
EOF

chmod go-w ~/Library/LaunchAgents/vpn.status.plist  
launchctl load ~/Library/LaunchAgents/vpn.status.plist

# If all went well then you should see a green circle in your menu bar
# indicating success.  If something didn't work then please let me know in the
# article comments.

Run the script. You will be shown the key fingerprint of the droplet and asked whether you want to continue connecting. Continue only if the fingerprint matches the one in the DigitalOcean console.

$ sh setup.sh

Once everything is set up, you can stop the VPN if you want to with the following command in Terminal:

$ sudo wg-quick down ~/Documents/vpn/utun.conf

To bring it up again:

$ sudo wg-quick up ~/Documents/vpn/utun.conf

Rebooting

I haven't figured out how to get the VPN to come back up automatically after a reboot. Please leave a comment below if you have a suggestion.

View or Post Comments