Building a Debian Based VPN Router - Part 1 - Networking


Interface Setup

I've been using systemd-networkd quite a bit recently, so this focuses on setting up the interfaces via that. I'm going to be setting up two VLANs on the router, 100 will be my protected vlan that will force all traffic via VPN and 200 will be a completely unprotected network that will go directly out over the WAN.

Interface Description Gateway
enp1s0f0 Virgin Media (WAN)  
enp1s0f1.100 Internal Protected 192.168.1.1/24
enp1s0f1.200 Internal Unprotected 192.168.2.1/24

First of all you want to make sure that Debian's network service script is disabled and you'll want to then enable systemd-networkd.

1
2
systemctl disable networking
systemctl enable systemd-networkd

Since I'm using Virgin it's best to spoof the MAC to that of your Virgin hub, setting MACAddress in the link section will set the MAC address of that interface.

Of course if you have a PtP connection or someting then change this, just remember if it isn't DHCP you'll have to specify your default gateway.

/etc/systemd/network/00-enp1s0f0.network

1
2
3
4
5
6
7
8
[Match]
Name=enp1s0f0

[Link]
MACAddress=00:11:22:33:44:55

[Network]
DHCP=true

Next we set up the netdev, this creates a virtual interface that we later tie to a physical one.

/etc/systemd/network/01-enp1s0f1.100.netdev

1
2
3
4
5
6
[NetDev]
Name=enp1s0f1.100
Kind=vlan

[VLAN]
Id=100

/etc/systemd/network/01-enp1s0f1.200.netdev

1
2
3
4
5
6
[NetDev]
Name=enp1s0f1.200
Kind=vlan

[VLAN]
Id=200

Now with those virtual interfaces set up, we tie them to the physical.

/etc/systemd/network/10-enp1s0f1.network

1
2
3
4
5
6
[Match]
Name=enp1s0f1

[Network]
VLAN=enp1s0f1.100
VLAN=enp1s0f1.200

/etc/systemd/network/20-enp1s0f1.100.network

1
2
3
4
5
[Match]
Name=enp1s0f1.100

[Address]
Address=192.168.1.1/24

/etc/systemd/network/20-enp1s0f1.200.network

1
2
3
4
5
[Match]
Name=enp1s0f1.200

[Address]
Address=192.168.2.1/24

Enable IP Forwarding

This is a router, so of course we need to be able to forward packets around!

1
sysctl net.ipv4.ip_forward=1

And of course you'll want to persist this to your sysctl.conf

1
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/10-ip_forward.conf

Setting up NAT

The next stage is we need to set up NAT to allow us external connectivity, obviously if your ISP is giving you multiple IP's here you can choose to do 1:1 NAT or something else, but for me I have a single external IP.

Since we're going to be dealing with iptables here it would be a good idea to allow us to persist those rules so go ahead and install iptables-persistent.

1
sudo apt install iptables-persistent

So first of all lets set up some basic rules before we even get on to NAT

1
2
3
iptables -t filter -A INPUT -i lo -j ACCEPT
iptables -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -i enp1s0f1.100 -p tcp --dport 22 -j ACCEPT

So here we're just saying allow any connections to loopback, allow any related or established packets, and allow any connections from our protected VLAN to port 22 (SSH).

At the moment this isn't really doing anything because our INPUT chains default policy is still to ACCEPT, so we need to change that policy so anything outside of those rules get dropped.

1
iptables -t filter -P INPUT DROP

This will then set our policy to drop for INPUT, if you've done everything correctly then you can still get access to your box via the protected vlan, if you can't then you're going to have to revert it. If you're looking for a way to do this safely then have a look at

1
man iptables-apply

If you're happy, save those changes to your rules file.

1
iptables-save > /etc/iptables/rules.v4

Right, now onto the actual NAT.

1
2
3
4
5
iptables -t nat -A POSTROUTING -o enp1s0f0 -j MASQUERADE
iptables -t filter -A FORWARD -i enp1s0f0 -o enp1s0f1.100 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A FORWARD -i enp1s0f1.100 -o enp1s0f0 -j ACCEPT
iptables -t filter -A FORWARD -i enp1s0f0 -o enp1s0f1.200 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A FORWARD -i enp1s0f1.200 -o enp1s0f0 -j ACCEPT

Super simple. We set up a MASQUERADE rule on our external connection, this is effectively source NAT, however with source NAT you need to specify an IP address. Since we're on a dynamic IP for this connection we use MASQUERADE instead which is kind of a shortcut for source NAT that uses the interfaces IP address.

If you are on a static IP you should really use SNAT here because there's an overhead with MASQUERADE where for every packet it has to check for the IP address to use.

Finally we then just say if we're forwarding from our internal networks to our external, ACCEPT those packets. Inversely accept packets back from the WAN if they're in a related or established state.

If everything has gone correctly at this point you should be able to get external connectivity, and you should save your rules at this point.

1
iptables-save > /etc/iptables/rules.v4

So at this point you basically have a really simple router that is doing NAT.

Summary

If you have this all working you can move on to the next part:

Comments