A guide for setting up your own private VPN service, and understanding and fixing a DNS leak.
While rolling your own Virtual Private Network (VPN) is far more complicated than choosing a VPN provider from someone’s “best VPN 2019” list, the more I learn about why someone should use a VPN at all, the less appealing the latter option becomes. Besides the dangers of trusting a fake VPN app or falling victim to a lookalike URL, even legit VPN service providers have pressures and motivations that may not be aligned with the privacy you hope to be purchasing.
Usually, the point of using a VPN is to gain a layer of privacy by disguising your location. If you aren’t currently using one, you can see what the Internet knows about where you are at the DNS leak test website. You’ll see a big hello, your IP address, and your location. If that’s a little unsettling, know that a VPN can help to shield your location and online activities from wandering eyes and opportunistic advertisers. The former might be a too-curious or even malicious public-WiFi-cafe-goer, but the latter, counterintuitively, might be your own household Internet Service Provider (ISP).
Using a VPN means that the Internet can’t easily see your location, and your ISP can’t see your unencrypted web traffic (and neither can your curious coffee shop neighbor). Your ISP can see the amount of data you’re sending, in its encrypted form, and that you’re sending it to your VPN server - but that’s all.
Unless you have a DNS leak.
If you are still using your ISP’s DNS server, they are still able to see all the URLs the server is resolving for you. So they’ll know you asked for
lastminutebackwax.com, although they won’t be able to decrypt the data that was exchanged with the site. (Is it just me, or does that seem even worse, somehow?)
Setting up your own instance and VPN service offers some peace of mind over trusting yet another company to do right with your data. Note that a VPN will not give you complete online anonymity; there are many other ways your Internet presence can be tracked and your location discovered. However, if properly set up, without DNS leaks, you’ll have about as much Internet privacy as can be afforded without using Tor.
This post will cover how to set up the OpenVPN Access Server product on AWS Marketplace, running on an Amazon EC2 instance. Then, we’ll look at how to fix a known NetworkManager bug in Ubuntu 18.04 that might cause DNS leaks. The whole process should take about fifteen minutes, so grab a ☕ and let’s do some adulting.
Note: IDs and IP addresses shown for demonstration in this tutorial are invalid.
The OpenVPN Access Server is available on AWS Marketplace. The Bring Your Own License (BYOL) model doesn’t actually require a license for up to two connected devices; to connect more clients, you can get bundled billing for five, ten, or twenty-five clients, or purchase a minimum of ten OpenVPN licenses a la carte for $15/device/year. For most of us, the two free connected devices will suffice; and if using an EC2 Micro instance, our set up will be AWS Free Tier eligible as well.
Start by clicking “Continue to Subscribe” for the OpenVPN Access Server, which will bring you to a page that looks like this:
Click “Continue to Configuration.”
You may notice that the EC2 instance type in the right side bar (and consequently, the Monthly Estimate) isn’t the one we want - that’s okay, we can change it soon. Just ensure that the “Region” chosen is where we want the instance to be located. Generally, the closer it is to the physical location of your client (your laptop, in this case), the faster your VPN will be. Click “Continue to Launch.”
On this page, we’ll change three things:
Different types of EC2 (Elastic Compute Cloud) instances will offer us different levels of computing power. If you plan to use your instance for something more than just this VPN, you may want to choose something with higher memory or storage capacity, depending on how you plan to use it. We can view each instance offering on the Amazon EC2 Instance Types page.
For simple VPN use, the
t2.micro instances are likely sufficient. Only the Micro instance is Free Tier eligible.
A Security Group is a profile, or collection of settings, that Amazon uses to control access to our instance. If you’ve set up other AWS products before, you may already have some groups with their own rules defined. We should be careful to understand the reasons for our Security Group settings, as these define how public or private our instance is, and consequently, who has access to it.
If we click “Create New Based on Seller Settings,” the OpenVPN server defines some recommended settings for a default Security Group.
The default recommended settings are all
0.0.0.0/0 for TCP ports 22, 943, 443, and 945, and UDP port 1194. OpenVPN offers an explanation of how the ports are used on their website. With the default settings, all these ports are left open to support various features of the OpenVPN server. We may wish to restrict access to these ports to a specific IP address or block of addresses (like that of your own ISP) to increase the security of our instance. However, if your IP address frequently changes (like when you travel and connect to a different WiFi network), restricting the ports may not be as helpful as we hope.
In any case, our instance will require SSH keys to connect to, and the OpenVPN server will be password protected. Unless you have other specific security goals, it’s fine to accept the default settings for now.
Let’s give the Security Group a name and brief description, so we know what it’s for. Then click “Save.”
The aforementioned SSH keys are access credentials that we’ll use to connect to our instance. We can create a key pair in this section, or you can choose a key pair you may already be using with AWS.
To create a new set of access credentials, click “Create a key pair in EC2” to open a new window. Then, click the “Create Key Pair” blue button. Once you give your key pair a name, it will be created and the private key will automatically download to your machine. It’s a file ending with the extension
.pem. Store this key in a secure place on your computer. We’ll need to refer to it when we connect to our new EC2 instance.
We can return to the previous window to select the key pair we just created. If it doesn’t show up, hit the little “refresh” icon next to the drop-down. Once it’s selected, hit the shiny yellow “Launch” button.
We should see a message like this:
Great stuff! Now that our instance exists, let’s make sure we can access it and start up our VPN. For a shortcut to the next step, click on the “EC2 Console” link in the success message.
Amazon’s Elastic IP Addresses provides us with a public IPv4 address controlled by our account, unlike the public IP address tied to our EC2 instance. It’s considered a best practice to create one and associate it with our VPN instance. If anything should go wrong with our instance, or if we want to use a new instance for our VPN in the future, the Elastic IP can be disassociated from the current instance and reassociated with our new one. This makes the transition seamless for our connected clients. Think of the Elastic IP like a web domain address that we register - we can point it at whatever we choose.
We can create a new Elastic IP address on the Amazon EC2 Console. If you clicked the link from the success message above, we’re already there.
If you have more than one instance, take note of the Instance ID of the one we’ve just launched.
In the left sidebar under “Network & Security,” choose “Elastic IPs.” Then click the blue “Allocate new address” button.
Choose “Amazon Pool,” then click “Allocate.”
Success! We can click “Close” to return to the Elastic IP console.
Now that we have an Elastic IP, let’s associate it with our instance. Select the IP address, then click “Actions,” and choose “Associate address.”
Ensure the “Instance” option is selected, then click the drop-down menu. We should see our EC2 instance ID there. Select it, then click “Associate.”
Success! Now that we’ll be able to access our VPN instance, let’s get our VPN service up and running.
First, we’ll need to connect to the EC2 instance via our terminal. We’ll use the private key we created earlier.
Open a new terminal window and navigate to the directory containing the private key
.pem file. We’ll need to set its permissions with:
sudo chmod 400 <name>.pem
Be sure to substitute
<name> with the name of your key.
This sets the file permissions to
-r-------- so that it can only be read by the user (you). It may help to protect the private key from read and write operations by other users, but more pertinently, will prevent AWS from throwing an error when we try to connect to our instance.
We can now do just that by running:
ssh -i <name>.pem openvpnas@<elastic ip>
openvpnas is set up by the OpenVPN Access Server to allow us to connect to our instance. Replace
<elastic ip> with the Elastic IP address we just associated.
We may get a message saying that the authenticity of our host can’t be established. As long as we’ve typed the Elastic IP correctly, we can go ahead and answer “yes” to the prompt.
Upon the initial connection to the OpenVPN instance, a set up wizard called “Initial Configuration Tool” should automatically run. (If, for some reason, it doesn’t, or you panic-mashed a button, we can restart it with
sudo ovpn-init –ec2.) We’ll be asked to accept the agreement, then the wizard will help to walk us through some configuration settings for our VPN server.
You may generally accept the default settings, however, there are a couple questions you may like to answer knowledgeably. They are:
Should client traffic be routed by default through the VPN? Should client DNS traffic be routed by default through the VPN? When asked for our “OpenVPN-AS license key”, we can leave it blank to use the VPN with up to two clients. If you’ve purchased a key, enter it here. Once the configuration wizard finishes running, we should see the message “Initial Configuration Complete!” Before we move on, we should set a password for our server’s administration account. To do this, run:
sudo passwd openvpn
Then enter your chosen password twice. Now we’re ready to get connected!
To close the ssh connection, type
To connect our client (in this case, our laptop) to the VPN and start reaping the benefits, we’ll need to do two things; first, obtain our connection profile; second, install the
We’ll need to download a connection profile for ourselves; this is like a personal configuration file with information, including keys, that the VPN server will need to allow our connection. We can do this by logging in with the password we just set at our Elastic IP address, port 943. This looks like:
https part is important; without it, the instance won’t send any data.
When we go to this URL, we may see a page warning us that this site’s certificate issuer is unknown or invalid. As long as we’ve typed our Elastic IP correctly, it’s safe to proceed. If you’re using Firefox, click “Advanced,” and then “Accept the Risk and Continue.” In Chrome, click “Advanced,” then “Proceed to …” the elastic IP.
Log in with the username
openvpn and the password we just set. We’ll now be presented with a link to download our user-locked connection profile:
When we click the link, a file named
client.ovpn will download.
openvpn daemon will allow our client to connect to our VPN server. It can be installed through the default Ubuntu repositories. Run:
sudo apt install openvpn
In order for OpenVPN to automatically start when we boot up our computer, we’ll need to rename and move the connection profile file. I suggest using a symlink to accomplish this, as it leaves our original file more easily accessible for editing, and allows us to store it in any directory we choose. We can create a symlink by running this command in the directory where our file is located:
sudo ln -s client.ovpn /etc/openvpn/<name>.conf
This creates a symbolic link for the connection profile in the appropriate folder for
systemd to find it. The
<name> can be anything. When the Linux kernel has booted,
systemd is used to initialize the services and daemons that the user has set up to run; one of these will now be OpenVPN. Renaming the file with the extension
.conf will let the
openvpn daemon know to use it as our connection file.
For now, we can manually start and connect to OpenVPN by running:
sudo openvpn --config client.ovpn
We’ll be asked for a username and password, which will be the same credentials we used before. Once the service finishes starting up, we’ll see “Initialization Sequence Complete.” If we now visit the DNS leak test website, we should see the Elastic IP and the location of our EC2 server. Yay!
If you’re on a later version of Ubuntu, you may check for DNS leaks by clicking on one of the “test” buttons. If all the ISPs shown are Amazon and none are your own service provider’s, congratulations! No leaks! You can move on to Step 3 in the second section below, after which, you’ll be finished.
If you’re using Ubuntu 18.04 LTS, however, we’re not yet done.
To see what a DNS leak looks like, click on one of the “test” buttons on the the DNS leak test page. When we do, we’ll see not only our Amazon.com IP addresses, but also our own ISP and location.
We can also see the leak by running
systemd-resolve --status in our terminal. Our results will contain two lines under different interfaces that both have entries for DNS Servers. It’ll look something like this:
Link 7 (tun0) Current Scopes: DNS LLMNR setting: yes MulticastDNS setting: no DNSSEC setting: no DNSSEC supported: no DNS Servers: 172.31.0.2 DNS Domain: ~. Link 3 (wlp4s0) Current Scopes: none LLMNR setting: yes MulticastDNS setting: no DNSSEC setting: no DNSSEC supported: no DNS Servers: 192.168.0.1 DNS Domain: ~.
The DNS leak problem in Ubuntu 18.04 stems from Ubuntu’s DNS resolver,
systemd-resolved, failing to properly handle our OpenVPN configuration. In order to try and be a good, efficient DNS resolver,
systemd-resolved will send DNS lookup requests in parallel to each interface that has a DNS server configuration, and then utilizes the fastest response. In our case, we only want to use our VPN’s DNS servers. Sorry,
systemd-resolved. You tried.
Luckily, there is a fix that we can implement. We’ll need to install a few helpers from the Ubuntu repositories, update our configuration file, then set up OpenVPN using NetworkManager. Let’s do it!
To properly integrate OpenVPN with
systemd-resolved, we’ll need a bit more help. In a terminal, run:
sudo apt install -y openvpn-systemd-resolved network-manager-openvpn network-manager-openvpn-gnome
This will install a helper script that integrates OpenVPN and
systemd-resolved, a NetworkManager plugin for OpenVPN, and its GUI counterpart for GNOME desktop environment.
We’ll need to edit the connection profile file we downloaded earlier. Since it’s symbolically linked, we can accomplish this by changing the
.ovpn file, wherever it’s stored. Run
vim <name>.ovpn to open it in Vim, then add the following lines at the bottom. Explanation in the comments:
# Allow OpenVPN to call user-defined scripts script-security 2 # Tell systemd-resolved to send all DNS queries over the VPN dhcp-option DOMAIN-ROUTE . # Use the update-systemd-resolved script when TUN/TAP device is opened, # and also run the script on restarts and before the TUN/TAP device is closed up /etc/openvpn/update-systemd-resolved up-restart down /etc/openvpn/update-systemd-resolved down-pre
We’ll use the GUI to set up our VPN with NetworkManager. Open up Network Settings, which should look something like this:
Then click the “+” button. On the window that pops up, counterintuitively, choose “Import from file…” instead of the OpenVPN option.
Navigate to, and then select, your
.ovpn file. We should now see something like this:
Add your username and password for the server (
openvpn and the password we set in the first section’s Step 3), and your user key password (the same one again, if you’ve followed this tutorial), then click the “Add” button.
Nearly there! Now that we’ve added the VPN as a NetworkManager connection, we’ll need to make a quick change to it. We can see a list of NetworkManager connections by running:
ls -la /etc/NetworkManager/system-connections/*
The one for our VPN is probably called
openvpn, so let’s edit it by running:
sudo vim /etc/NetworkManager/system-connections/openvpn
[ipv4], we’ll need to add the line
dns-priority=-42. It should end up looking like this:
Setting a negative number is a workaround that prioritizes this DNS server. The actual number is arbitrary (
-1 should also work) but I like 42. ¯\_(ツ)_/¯
In a terminal, run:
sudo service network-manager restart
Then in the Network Settings, click the magic button that turns on the VPN:
Finally, visit the DNS leak test website and click on “Extended test” to verify the fix. If everything’s working properly, we should now see a list containing only our VPN ISP.
And we’re done! Congratulations on rolling your very own VPN server and stopping DNS leaks with OpenVPN. Enjoy surfing in (relative) privacy. Now your only worry at the local coffeeshop is who’s watching you surf from the seat behind you.