Build a VPN With OpenVPN on Ubuntu

It’s 2017, and using a VPN has become a no-brainer. Between the loads of external privacy concerns and your own ISP being able to sell your browsing history, there’s really no justifying not using one.

Sure, you can pay one of the hundreds of VPN services out there, but once again, you’re relying on someone else with your data. Most are great, in reality, but if you want complete control, you can build your own VPN on a Virtual Private Server(VPS) or rent your own private server, if you feel like being really hardcore with it.

All that you need to build a VPN is the open source OpenVPN software and Linux(or BSD). The configuration can be involved, but it’s not impossible with someone with even basic Linux skills to pull off on a distribution like Ubuntu.

For this guide, you’re going to need a VPS running Ubuntu. You can pick one up very easily from someone like DigitalOcean or Linode. Follow their basic security guides for getting set up. Make sure that you don’t make basic mistakes like allowing root access over SSH.

Also, keep in mind that this you’re going to be doing this entire setup in the command line over SSH to your VPS. There isn’t anything that requires a crazy amount on Linux knowledge, but be prepared to be typing instead of clicking.

Getting What You Need

Ubuntu packages and distributes OpenVPN in its repositories. You only need to use apt to install it. You’ll also need the tool for generating encryption keys. Install them both.

$ sudo apt install openvpn easy-rsa

Set Up The Firewall

Next, you need to take care of the firewall. It’s an important piece in keeping your VPN secure and preventing both data leakage and unwanted access.

Iptables is the main firewall for Linux, and it’s your best option for controlling access to Ubuntu’s ports. You’ll already have it installed, so you can get started setting up your firewall rules.

Find The Interface

Before you start writing rules into iptables, find out what interface your server is connected to the Internet with. Run ifconfig to display your network interfaces. The one that has an inet addr: matching the IP address that you’re connected to is the right interface.

Iptables Basics

It’s usually not a good idea to randomly copy and paste things into the terminal from the Internet. This is especially true when you’re dealing with security topics. So, take some time here to learn a bit about iptables rules before you start entering them.

Take a look at this example of an iptables rule.

-A INPUT -i eth0 -p tcp -m state –state ESTABLISHED –sport 443 -j ACCEPT

Alright, so -A means that you’re going to be appending a new rule. Then INPUT means that it will concern input to your server. There is also an OUTPUT. The -i flag tells iptables which interface this rule is for. You can specify which protocol the rule is for with -p. This rule handles tcp. -m specifies a condition that a connection must meet. In this case it must match the state that’s specified. Of course, then –state specifies a state, in this case an ESTABLISHED connection. The next part tells iptables which port this rule is for. It’s port 443, the HTTPS port, here. The last flag is -j. It stands for “jump,” and it tells iptables what to do with the connection. If this connection met all of the requirements in the rule, iptables would ACCEPT it.

Set Up Your Rules

So, you should have a general idea how iptables rules work now. The rest of this section will tell you how to set up your rules piece by piece.

The best way to create a set of iptables rules is to create a file containing all of them. Then, you can import it all into iptables at once. Setting rules one-by-one can get confusing, especially if you’re starting a new set of rules from scratch.

Create a file in the /tmp directory to build your rules.

$ vim /tmp/ipv4

Start that file off with *filter. This tells iptables that what follows is going to be rules for packet filtering.

Loopback

The first section of rules lock down the loopback interface. They tell iptables that the server should accept traffic from itself on the loopback interface. It should also reject traffic coming from itself that not coming from loopback.

-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT
-A OUTPUT -o lo -j ACCEPT

Ping

Next, allow ping. You should be able to ping your server to make sure that it’s online in case it’s unreachable otherwise. In this case, only echo requests are allowed, and the server will allow itself to send ICMP output.

-A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT
-A INPUT -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT

SSH

You need SSH. That’s the only way that you can reach your server. The SSH rules are specific to your internet interface, so make sure you substitute eth0 for whichever interface your server is actually using.

It also might be a good idea to change your SSH connections off of port 22, since that’s the default that potential attackers would try. If you do so, make sure to change it in your iptables rules too.

-A INPUT -i eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 22 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state ESTABLISHED --sport 22 -j ACCEPT

OpenVPN

This next piece allows traffic to and from the OpenVPN server over UDP.

-A INPUT -i eth0 -p udp -m state --state NEW,ESTABLISHED --dport 1194 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m state --state ESTABLISHED --sport 1194 -j ACCEPT

DNS

Now, allow DNS connections over UDP and TCP. You want your VPN to handle DNS, not your ISP. That’s part of the reason you’re setting up a VPN in the first place.

-A INPUT -i eth0 -p udp -m state --state ESTABLISHED --sport 53 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m state --state NEW,ESTABLISHED --dport 53 -j ACCEPT
-A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 53 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 53 -j ACCEPT

HTTP/S

In order for Ubuntu to be able to update itself, you need to add a set of rules to allow outgoing connection of HTTP and HTTPS. Note that these rules only allow the server to initiate HTTP connections, so you cannot use it as a web server or connect to it over port 80 or port 443

-A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 80 -j ACCEPT
-A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 443 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 80 -j ACCEPT
-A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 443 -j ACCEPT

NTP

To keep your server clock running properly, you’re going to need NTP. NTP allows your server to synchronize with timeservers around the world. Having an incorrect clock on your server can cause connection issues, so running NTP is a good idea. Once again, you should only accept outgoing and already established connections.

-A INPUT -i eth0 -p udp -m state --state ESTABLISHED --sport 123 -j ACCEPT
-A OUTPUT -o eth0 -p udp -m state --state NEW,ESTABLISHED --dport 123 -j ACCEPT

TUN

Unblock the TUN interface which OpenVPN uses to tunnel traffic.

-A INPUT -i tun0 -j ACCEPT
-A FORWARD -i tun0 -j ACCEPT
-A OUTPUT -o tun0 -j ACCEPT

You need to allow TUN to forward traffic to your regular interface for the VPN. You’ll find that IP address in the OpenVPN configuration. If you change it in the configuration, change it in your rules too.

-A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

Logging

It’s a good idea to keep logs of everything that gets rejected by iptables. In this case, it means anything that doesn’t fit into any of these rules. Logs let you see if there is any malicious activity or any attempts to do anything nefarious against your server.

-A INPUT -m limit –limit 3/min -j LOG –log-prefix “iptables_INPUT_denied: ” –log-level 4
-A FORWARD -m limit –limit 3/min -j LOG –log-prefix “iptables_FORWARD_denied: ” –log-level 4
-A OUTPUT -m limit –limit 3/min -j LOG –log-prefix “iptables_OUTPUT_denied: ” –log-level 4

Reject Everything Else

Finally, you need to block anything that doesn’t fit into your rules. That’s really the purpose of having a firewall in the first place.

-A INPUT -j REJECT
-A FORWARD -j REJECT
-A OUTPUT -j REJECT

Close out the file with COMMIT to tell iptables to commit all of the rules.

OpenVPN iptables rules

NAT Masquerading

You need the connections from the VPN to look like they’re coming from the server itself. This piece can’t be included in the regular iptables file because it uses a different table. That’s alright, though, it’s just one line.

$ sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

Forward IPv4 Traffic

You’re going to need to enable forwarding of IPv4 traffic, so it can pass between the VPN and your server’s actual network interface. Open /etc/sysctl.d/99-sysctl.conf with sudo.

Find the line below and uncomment it by removing the #.

net.ipv4.ip_forward=1

Stop All IPv6 Connections

Sorry, you’re not done with iptables yet. You need to block all IPv6 traffic. This OpenVPN server will only support IPv4, which is fine, since you’re not going to run into a situation where you need IPv6. As a result, any IPv6 connections can potentially leak information, which is the opposite of what you want when using a VPN.

Before setting the rules for iptables, you have to disable IPv6 everywhere else on the system.

Add the following lines to /etc/sysctl.d/99-sysctl.conf. If you closed it from the previous section, reopen it with sudo.

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1

Activate your changes.

$ sudo sysctl -p

Comment all of the IPv6 lines in /etc/hosts. You’re going to need sudo here too.

#::1     ip6-localhost ip6-loopback
#fe00::0 ip6-localnet
#ff00::0 ip6-mcastprefix
#ff02::1 ip6-allnodes
#ff02::2 ip6-allrouters

At last, you can write the IPv6 iptables rules. Create a file for them at /tmp/ipv6.

*filter

-A INPUT -j REJECT
-A FORWARD -j REJECT
-A OUTPUT -j REJECT

COMMIT

See, they’re simple. Reject everything.

Import and Save to Iptables

You need to import those rules in order for them to do anything. So, now’s the time to do that.

Start off by clearing out everything else that’s there. You don’t want any old rules getting in the way.

$ sudo iptables -F && sudo iptables -X

Import both your IPv4 and IPv6 rules.

$ sudo iptables-restore < /tmp/ipv4
$ sudo ip6tables-restore < /tmp/ipv6

You probably never want to do that again. So, you’re going to need a new package to save your rules permanently.

$ sudo apt install iptables-persistent

During the installation, the package will ask you to save your existing rules. Answer “Yes.”

If you make changes later on, you can update your saved configurations too.

$ sudo service netfilter-persistent save

It took a while, but your firewall is ready to go. On the next page, we’re going to be tackling creating the necessary encryption keys.

Click here: Next Page