Having previously made it possible to connect to a LAN website via VPN (Wireguard) I wanted to go further and be able to connect to everything on my LAN. I felt it would be smart to add some firewall rules to the computer hosting the VPN server which also hosts pi-hole and unbound. Debian defaults have all rules set to ACCEPT which means the firewall is active but not doing anything.
# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
There are many hits when you search for basic firewall rules so I opted for a simple one 1 that covers the important parts e.g. HTTP(S), DNS, SSH.
Most examples I found assume the primary interface is eth0 which is often not the case so I got the network interface names with ip a which in my case lists three interfaces, the first two interfaces should be visible on every install, the third one is created during the install of Wireguard with pivpn.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 50:65:f3:30:c0:f5 brd ff:ff:ff:ff:ff:ff
inet 192.168.178.237/24 brd 192.168.178.255 scope global dynamic eno1
valid_lft 70797sec preferred_lft 70797sec
inet6 fe80::5265:f3ff:fe30:c0f5/64 scope link
valid_lft forever preferred_lft forever
5: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.235.209.1/24 scope global wg0
valid_lft forever preferred_lft forever
enp1s0 is the name of the interface the network cable is plugged into and wg0 is the VPN interface. I've changed the default ssh port on this box so the 22 below needs to be adjusted as needed. This is the DNS server for the whole house so any wrong moves here would take down the internet and lead to instant complaints from my family so I kept the DEFAULT variable at ACCEPT while testing.
This script should be saved as firewall.sh for example and made executable with chmod +x
#!/bin/bash
ETH0=enp1s0
WG0=wg0
IPTABLES=/usr/sbin/iptables
SAVE=/usr/sbin/iptables-save
RESTORE=/usr/sbin/iptables-restore
COMMENT="-m comment --comment"
DEFAULT=DROP
# Save existing rules
$SAVE > /etc/iptables/rules.v4
# Flush all existing rules
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
# Loopback
$IPTABLES -A INPUT -i lo -j ACCEPT
$IPTABLES -A OUTPUT -o lo -j ACCEPT
# Established
$IPTABLES -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
# Drop invalid
$IPTABLES -A INPUT -m conntrack --ctstate INVALID -j DROP
# Incoming SSH --> This is the most important one for remote operation
$IPTABLES -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT $COMMENT "Allow SSH input"
$IPTABLES -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT $COMMENT "Allow SSH output"
# Outgoing HTTPS
$IPTABLES -A OUTPUT -o $ETH0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT -i $ETH0 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
# Outgoing HTTP
$IPTABLES -A OUTPUT -o $ETH0 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT -i $ETH0 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
# Outgoing DNS
$IPTABLES -A OUTPUT -p udp -o $ETH0 --dport 53 -j ACCEPT
$IPTABLES -A INPUT -p udp -i $ETH0 --sport 53 -j ACCEPT
# Incoming DNS
$IPTABLES -A INPUT -p udp -i $ETH0 --dport 53 -j ACCEPT
# Outgoing Ping
$IPTABLES -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
$IPTABLES -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
# Access pi-hole web interface
$IPTABLES -A INPUT -p tcp -i $ETH0 --dport 80 -j ACCEPT
$IPTABLES -A INPUT -p tcp -i $ETH0 --dport 443 -j ACCEPT
# Allow the Wireguard VPN to use Pi-hole if installed on LAN
$IPTABLES -t nat -I POSTROUTING 1 -s 10.125.245.1/24 -o $ETH0 -j MASQUERADE
$IPTABLES -I INPUT 1 -i $WG0 -j ACCEPT
$IPTABLES -I FORWARD 1 -i $ETH0 -o $WG0 -j ACCEPT
$IPTABLES -I FORWARD 1 -i $WG0 -o $ETH0 -j ACCEPT
$IPTABLES -I INPUT 1 -i $ETH0 -p udp --dport 51820 -j ACCEPT $COMMENT "Open Wireguard port"
# Now set default rules to drop anything that gets this far
$IPTABLES -P INPUT $DEFAULT
$IPTABLES -P FORWARD $DEFAULT
$IPTABLES -P OUTPUT $DEFAULT
Additionally we need to enable forwarding if not done; running cat /proc/sys/net/ipv4/ip_forward gave an output of 1 indicating that forwarding is enabled as needed, any other output means you should run echo 1 > /proc/sys/net/ipv4/ip_forward as root by doing sudo su
Note that during the install of Wireguard, pivpn will create the necessary iptables rules when it detects a pi-hole install and if you reply yes when it asks if you want VPN clients to use pi-hole for DNS. We are actually deleting those and recreating them 2.
With all of this done the output of sudo iptables -S looks like this:
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT DROP
-A INPUT -i enp1s0 -p udp -m udp --dport 51820 -m comment --comment "Open Wireguard port" -j ACCEPT
-A INPUT -i wg0 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -m comment --comment "Allow SSH input" -j ACCEPT
-A INPUT -i enp1s0 -p tcp -m tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -i enp1s0 -p tcp -m tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -i enp1s0 -p udp -m udp --sport 53 -j ACCEPT
-A INPUT -i enp1s0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -i enp1s0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i enp1s0 -p tcp -m tcp --dport 443 -j ACCEPT
-A FORWARD -i wg0 -o enp1s0 -j ACCEPT
-A FORWARD -i enp1s0 -o wg0 -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp --sport 22 -m conntrack --ctstate ESTABLISHED -m comment --comment "Allow SSH output" -j ACCEPT
-A OUTPUT -o enp1s0 -p tcp -m tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -o enp1s0 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -o enp1s0 -p udp -m udp --dport 53 -j ACCEPT
-A OUTPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
and the output of sudo iptables -S -t nat looks like this:
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-A POSTROUTING -s 10.125.245.0/24 -o enp1s0 -j MASQUERADE
Last step is to forward port 51820 on the router to the IP address of the Wireguard server.
Testing it inside the house was done from my phone set to 5G and verified by being able to ssh to a device which is not port forwarded from the router when connected to the VPN and not being able to connect when not in the VPN.