Distributions

This guide is tested for Debian 9 Stretch Linux, but it may work with other recent Debian versions.

Requirements

  • This guide assumes that you're running Debian on a VPS or a remote server, since that's the most likely scenario for a VPN.
  • A working install of Debian Stretch with root access

Difficulty

MEDIUM

Conventions

  • # - requires given command to be executed with root privileges either directly as a root user or by use of sudo command
  • $ - given command to be executed as a regular non-privileged user

Configuring Iptables

Setting up your own VPN is no small task, but there are plenty of reasons that you'd want to do it. For one, when you run your own VPN, you have complete control of it and know exactly what it's doing.

Security is an important factor for VPNs. It's possible to set up a simple one in a few minutes, but it won't be secure at all. You need to take the appropriate steps to ensure that both the server and your connections remain private and encrypted.

Before embarking down this road, you may want to consider encrypting your disks, beefing up kernel security with SELinux or PAX, and making sure everything else is locked down.

Iptables is a big part of server security. You need iptables to ensure that information doesn't leak out of your VPN. Iptables also works to prevent unauthorized connections. So, the first step in setting up a VPN on Debian is setting up iptables.

Find Your WAN Interface

Before you can start writing your iptables rules, you need to know which interface you're writing them for.

Use ifconfig or ip a to search for the interface that your server is connected to the Internet with.

The rest of this guide will refer to that interface as eth0, but that probably won't be yours. Make sure to swap in the name of your server's network interface instead.

Creating The Iptables Rules

Every Linux user and admin loves writing iptables rules, right? It's not going to be that bad. You'll compose a file with all of the commands and just restore it into iptables.

Create your file. You can make it somewhere that you want to save or just dump it in /tmp. Iptables will save your rules anyway, so /tmp is fine.
$ vim /tmp/v4rules
Start the file off by adding *filter to let iptables know that these are filter rules.

Yes, there will be an IPv6 one too, but it'll be much shorter.

Loopback Rules

Start off with the simplest set of rules, the loopback interface ones. These just tell iptables to only accept looback traffic originating from localhost.
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT
-A OUTPUT -o lo -j ACCEPT

Allowing Ping

Next, you probably want to be able to ping your server. This group of rules allows ping through.
-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 Setup

You should probably change SSH off of port 22, so let your rules reflect that.
-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

Allow OpenVPN Through

Obviously, you're going to want to allow OpenVPN traffic through. This guide is going to use UDP for OpenVPN. If you choose to go with TCP, let the rules reflect that.
-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

You're also going to want to allow DNS traffic through your VPN server. This will be through both UDP and TCP.
-A INPUT -i eth0 -p udp -m state --state ESTABLISHED --sport 53 -j ACCEPT
104 -A OUTPUT -o eth0 -p udp -m state --state NEW,ESTABLISHED -- dport 53 -j ACCEPT
105 -A INPUT -i eth0 -p tcp -m state --state ESTABLISHED --sport 53 -j ACCEPT
106 104 -A OUTPUT -o eth0 -p tcp -m state --state NEW,ESTABLISHED --dport 53 -j ACCEPT

HTTP/S For Updates

It might seem odd to allow HTTP/S traffic, but you do want Debian to be able to update itself, right? These rules allow Debian to initiate HTTP requests, but not receive them from the outside.
-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 Sync Your Clock

Assuming that you're not going to manually synchronize your server clock and the client clocks, you're going to need NTP. Allow it too.
-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 To Tunnel Through The VPN

This guide uses TUN to tunnel through the VPN, if you're using TAP, adjust accordingly.
-A INPUT -i tun0 -j ACCEPT
-A FORWARD -i tun0 -j ACCEPT
-A OUTPUT -o tun0 -j ACCEPT
For the VPN to forward your traffic to the Internet, you need to enable forwarding from TUN to your physical network interface.
-A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

Log Blocked Traffic

You should probably have iptables log the traffic that it blocks. This way, you are aware of any potential threats.
-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 All Other Traffic

Now that you're logging everything that doesn't fit into the existing rules, reject it.
-A INPUT -j REJECT
-A FORWARD -j REJECT
-A OUTPUT -j REJECT
Don't forget to close out your file with COMMIT.

NAT

This next part requires a different table. You can't add it to the same file, so you'll just have to run the command manually.

Make traffic from the VPN masquerade as traffic from the physical network interface.
# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE

Block All IPv6 Traffic

Traffic can leak out over IPv6, and there really isn't any need to use IPv6 right now. The easiest thing to do is to shut it down entirely. Create another file and throw in the rules to reject all IPv6 traffic.
$vim /tmp/v6rules
*filter

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

COMMIT

Commit Everything

Start off by flushing out all existing iptables rules.
# iptables -F && iptables -X
Import each of the rules files that you created.
# iptables-restore < /tmp/v4rules
# iptables-restore < /tmp/v6rules

Making It Stick

Debian has a package that will handle automatically loading your iptable rules, so you don't have to create a cron job or anything like that.
# apt install iptables-persistent
The installation process will ask you if you want to save your configurations. Answer, "Yes."

In the future, you can update your rules by running the following command.
# service netfilter-persistent save

Additional Configuration

There are a couple more things that you need to do in order to get all of your network interfaces working as needed.

First, open up /etc/hosts and comment out all of the IPv6 lines.

Next, open /etc/sysctl.d/99-sysctl.conf. Find and uncomment the following line.
net.ipv4.ip_forward=1
Add these next lines to disable IPv6 completely.
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
Finally, apply your changes.
# sysctl -p

What's Next

That's the first part down. Your server's firewall is now ready to run OpenVPN, and your networking is all aligned properly too. The next step is to create a certificate authority to handle all of your encryption keys. It's not lengthy a process as this was, but it's just as important.

Certification Authority

Use Easy-RSA to establish the certificate authority that you'll use to create and the encryption keys for your OpenVPN server. This is the second part in configuring an OpenVPN server on Debian Stretch.

VPNs rely on encryption. It's absolutely vital that they encrypt their connections with clients as well as the connection process itself.

In order to generate the keys necessary for encrypted communication, you need to establish a certificate authority. It's really not that difficult, and there are tools that simplify the process further.

Installing the Packages

Before you get started, install OpenVPN and Easy-RSA.
# apt install openvpn easy-rsa

Set Up The Directory

The OpenVPN package created a directory for itself at /etc/openvpn. That's where you can set up the certificate authority. Easy-RSA includes a script that automatically creates a directory with everything that you need. Use it to create your certificate authority directory.
# make-cadir /etc/openvpn/certs
Enter that directory and create a soft link between the latest OpenSSL config with openssl.cnf.
# ln -s openssl-1.0.0.cnf openssl.cnf

Set The Variables

Inside the folder is a file called, vars. That file contains the variables that Easy-RSA will use to generate your keys. Open it up. There are a few values that you need to change. Start by finding the KEY_SIZE variable and change it's value to 4096.
export KEY_SIZE=4096
Next, find a block of information concerning the location and identity of your certificate authority.
export KEY_COUNTRY="US"
export KEY_PROVINCE="CA"
export KEY_CITY="SanFrancisco"
export KEY_ORG="Fort-Funston"
export KEY_EMAIL="This email address is being protected from spambots. You need JavaScript enabled to view it."
export KEY_OU="MyOrganizationalUnit"
Change the values to correspond to you. The last variable that you need to find is the KEY_NAME
export KEY_NAME="VPNServer"
Name it something identifiable.

Create The Authority Keys

Easy-RSA includes scripts to generate the certificate authority. Load the variables first.
# source ./vars
A warning message will pop up in the terminal telling you that clean-all will erase your keys. You don't have any yet, so it's alright.
# ./clean-all
You can now run the script to actually generate your certificate authority. The script will ask you questions about the keys that you're generating. The default answers will be the variables that you already entered. You can safely smash "Enter." Just remember to enter a password if you want to and answer "Yes" to the last two questions.
# ./build-ca

Create A Server Key

Those keys that you made were for the certificate authority itself. You need a key for the server too. Yet again, there's a script for that.
# ./build-key-server server

Generate a Diffie-Hellman PEM

You need to generate a Diffie-Hellman PEM which OpenVPN will use to create secure client sessions keys. Easy-RSA provides a script for this too, but it's just easier to use plain OpenSSL. Since the goal here is security, it's best to generate a 4096bit key. It's going to take some time to generate, and it might slow down the connection process a bit, but the encryption will be reasonably strong.
# openssl dhparam 4096 > /etc/openvpn/dh4096.pem

Generate An HMAC Key

Yeah, you need another encryption key. OpenVPN uses HMAC keys to sign the packets it uses in the TLS authentication process. By signing those packets, OpenVPN can guarantee that only packets originating from a machine with the key are accepted. It just adds another layer of security. The utility for generating your HMAC key is actually built into OpenVPN itself. Run it.
# openvpn --genkey --secret /etc/openvpn/certs/keys/ta.key

What's Next

Creating strong encryption is easily one of the most important aspects of setting up an OpenVPN server. Without good encryption, the whole process is basically meaningless. By this point, you're finally ready to configure the server itself. The server configuration is actually less complicated than what you've done so far, so congratulations.

OpenVPN Sever

Configure the OpenVPN server using the encryption keys that you generated in the previous section of the guide. This is the third part in configuring an OpenVPN server on Debian Stretch.

Now, you've arrived at the main event. This is the actual OpenVPN server configuration. Everything that you've done so far was absolutely necessary, but none of it has touched OpenVPN itself, until now.

This section is entirely concerned with configuring and running the OpenVPN server, and it's actually less complicated than you're probably thinking.

Get The Base Config

OpenVPN has made this process very easy. The package that you installed came with sample configuration files for both clients and the server. You just need to unzip the server one into you /etc/openvpn directory.
# gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf
Open it in your favorite text editor and get ready to start changing things.

Use Your Keys

Once you're inside the file, you'll see that everything is filled out with reasonable defaults, and there are loads of comments that provide excellent documentation of what everything does. The first thing that you need to find is the section to add your certificate authority and server keys. The variables are ca, cert, and key. Set them equal to the full path of each of those files. It should look like the example below.
ca /etc/openvpn/cert-auth/keys/ca.crt
cert /etc/openvpn/cert-auth/keys/server.crt
key /etc/openvpn/cert-auth/keys/server.key  # This file should be kept secret
The next part that you need to find is the Diffie-Hellman .pem When you're done, it should look like this:
dh dh4096.pem
Finally, find tls-auth for your HMAC key.
tls-auth /etc/openvpn/cert-auth/keys/ta.key 0 # This file is secret
Yes, leave the 0 on there.

Beef Up Security

The encryption settings in the configuration file are alright, but they could be much better. It's time to enable better encryption settings.

Find the section that begins with, # Select a cryptographic cipher. That's where you need to add the following line below he existing commented options.
cipher AES-256-CBC
It's not one of the listed options there, but it is supported by OpenVPN. That 256bit AES encryption is probably the best one offered by OpenVPN.

Scroll to the end of the file. The next two options aren't already in the configuration, so you need to add them.

First, you need to specify a strong authentication digest. This is the encryption that OpenVPN will use for user authentication. Choose SHA512.
# Auth Digest
auth SHA512
Next, limit the ciphers that OpenVPN will use to stronger ones. It's best to limit it as far as reasonably possible.
# Limit Ciphers
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA

Direct Traffic

All of the encryption stuff is out of the way. It's time to do some routing. You need to tell OpenVPN to handle redirecting traffic and DNS.

Start by redirecting traffic. Find the line below and uncomment it.
push "redirect-gateway def1 bypass-dhcp"
In order to route DNS through OpenVPN, you need to give it DNS options. These lines are already there and commented too. Uncomment them. If you want to use a different DNS server, you can change the IP to that DNS too.
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

Set Up An OpenVPN User

OpenVPN runs as root by default. That's a pretty terrible idea. If OpenVPN is compromised, the whole system's screwed. There are a couple of commented lines to run OpenVPN as "nobody," but "nobody" is usually running other services too. If you don't want OpenVPN to have access to anything but OpenVPN, you need to run it as its own unprivileged user.

Create a system user for OpenVPN to run as.
# adduser --system --shell /usr/sbin/nologin --no-create-home openvpn
Then, you can edit the configuration file by uncommenting the lines that run OpenVPN as "nobody," and replace it with the user name that you just made.
user openvpn
group nogroup

Send Logs To Null

There are two options when it comes to logs, and they both have their merits. You can log everything as normal and have the logs to loop back on at a later date, or you can be paranoid and log to /dev/null.

By logging to /dev/null, you're erasing any record of the clients that connect to the VPN and where they go. Even though you control your VPN, you may want to go this route if you're trying to be more privacy-minded.

If you want to destroy your logs, find the status, log, and log-append variables and point them all at /dev/null. It should look similar to the example below.
status /dev/null

…

log         /dev/null
log-append  /dev/null
That's the last part of the configuration. Save it, and get ready to run your server.

Run Your Server

There are actually two services that you need to start to spin up OpenVPN on Debian Stretch. Start them both up with systemd.
# systemctl start openvpn
# systemctl start openvpn@server
Verify that they're running properly.
# systemctl status openvpn*.service
Enable them both to run at startup.
# systemctl enable openvpn
# systemctl enable openvpn@server
You now have a running VPN server on Debian Stretch!

What's next

You're here. You've done it! Debian is now running OpenVPN behind a secure firewall, and it's ready for clients to connect. In the next section, you'll set up your first client and connect it to your server.

OpenVPN Client

Configure and OpenVPN client to connect to the newly configured OpenVPN server. This is the fourth and final part in configuring an OpenVPN server on Debian Stretch.

Now that your server is running, you can set up a client to connect to it. That client can be any device that supports OpenVPN, which is nearly anything. There are somethings that you need to do on the server first to hand off to the client, but after that, it's all about setting up that connection.

Create Client Keys

Start off by making a set of client keys. The process is almost identical to the one you used to make the server keys. cd into the certificate authority directory, set the source from the variables file and build the keys.
# cd /etc/openvpn/certs
# source ./vars
# ./build-key firstclient
You can name the client key whatever you choose. Again, the script will ask you a series of questions. The defaults should be good for everything.

Client Configuration File

OpenVPN provides example client configurations in addition to the server ones. Crate a new directory for your client configuration and copy the example in.
# mkdir /etc/openvpn/clients
# cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/clients/client.ovpn
Open the file in your text editor of choice.

Remote Host

Find the line with the remote variable. Set it equal to your server's IP.
remote 192.168.1.5 1194

Become Nobody

There's no training with the Faceless Men required. Just find an uncomment the lines below.
user nobody
group nogroup

Set Up Your Keys

You have to tell the client configuration where to find the keys that it needs too. Find the following lines and edit them to match what you've set up.
ca ca.crt
cert firstclient.crt
key firstclient.key
Make sure to use the actual names of the client cert and key. The path is fine. You'll be putting it all in the same directory. Find and uncomment the line for HMAC.
tls-auth ta.key 1

Specify Encryption

The client needs to know what encryption the server is using. Just like the server, a couple of these lines need to be added in. Find the cipher variable. It's commented. Uncomment it and add in the cipher that you used on the server.
cipher AES-256-CBC
Add in the authentication digest and the cipher restrictions at the end of the client configuration.
# Authentication Digest
auth SHA512

# Cipher Restrictions
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA
Save your configuration and exit.

Send The Client A Tarball

You have to pack up your client configuration and keys in a tarball and send them to the client. Load everything into one tarball to simplify things on the client end.
# tar cJf /etc/openvpn/clients/firstclient.tar.xz -C /etc/openvpn/certs/keys ca.crt firstclient.crt firstclient.key ta.key -C /etc/openvpn/clients/client.ovpn
Now, you can transfer that tarball to your client however you choose.

Connect

Assuming that your client is a Debian distribution, the connection process is very simple. Install OpenVPN like you did on the server.
# apt install openvpn
Extract your tarball into the /etc/openvpn directory that the installation created.
# cd /etc/openvpn
# tar xJf /path/to/firstclient.tar.xz
You may need to rename client.ovpn to openvpn.conf. You'll get an error on startup if you do. Start and enable OpenVPN with systemd.
# systemctl start openvpn
# systemctl enable openvpn

Conclusion

You have a working VPN server and a connected client! You can follow the same procedure detailed in this guide for your other clients too. Make sure to create separate keys for each one. You can use the same config file, though.

You also might want to make sure that everything is working properly. Head over to https://dnsleaktest.com" to make sure that your IP patches the server, and you're not using your IPS's DNS.