Objective

Configure iptables to be an effective firewall on a Debian Stretch system running an OpenVPN server. This is the first part in configuring an OpenVPN server on Debian Stretch.

Distributions

This guide is tested for Debian 9(Stretch), 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

Introduction

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 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
-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 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

Closing Thoughts

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.