Wireguard with Pi-Hole

From KG7QIN's Wiki
Revision as of 06:59, 2 July 2024 by Kg7qin (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Setting up a Wireguard VPN with Pi Hole DNS filtering

This was originally setup on a small $6/mo Debian 12 VPS as a test.

Note: You will need to run the commands shown as root or wrap in sudo

SSH Setup

1. Setup SSH on the system to only allow public key logins (disabling password login). This is important to help keep it secure.

Wireguard Install

2. Install Wireguard using the Wireguard Road Warrior Install. Follow the prompts to create a user and copy the profile to the device that will use this for VPN access. For DNS, choose the Quad 9 option.

Pi-Hole Install

3. Install Pi-Hole on your system:

  • Install required packages:
apt-get install wget curl net-tools gamin lighttpd lighttpd-mod-deflate
  • Run the Pi-hole installer with:
curl -sSL https://install.pi-hole.net | PIHOLE_SKIP_OS_CHECK=true sudo -E bash
  • Follow the prompts to configure

Fail2Ban Install

4. Install fail2ban on your system and ensure SSH is setup for blocking (default on Debian fail2ban installs):

apt-get install fail2ban
  • And ensure Fail2ban is enabled:
systemctl enable fail2ban

Configuration scripts

5. Copy the rc.local script to /etc/rc.local:

rc.local

#!/bin/sh
#Hide Pihole admin interface behind VPN only access
INET_IF=enp1s0

ManagementIP="10.7.0.0/24"
LocalIP="<Your System's Local IP>"

#Flush and zero all tables
modprobe ip_tables
modprobe ipt_limit
modprobe iptable_mangle
modprobe ipt_state
modprobe ipt_LOG
modprobe iptable_filter

iptables -F INPUT
iptables -F FORWARD
iptables -t nat -F POSTROUTING
iptables -t nat -F PREROUTING

#init the log-and-drop chain
iptables -F log-and-drop
iptables -X log-and-drop
iptables -N log-and-drop

iptables -F log-and-reject
iptables -X log-and-reject
iptables -N log-and-reject

echo "all tables flushed and dropped"
 
# Specific chain used for logging packets before blocking them
iptables -A log-and-drop -j LOG --log-prefix "[IPTables] Drop "
iptables -A log-and-drop -j DROP

# Specific chain used for logging packets before blocking them
iptables -A log-and-reject -j LOG --log-prefix "[IPTables] Reject "
iptables -A log-and-reject -j REJECT

echo "logging chains setup"

#Drop fragmented packets
iptables -A INPUT -f -j DROP
iptables -A FILTERS -f -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
iptables -A FILTERS -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
iptables -A FILTERS -p tcp --tcp-flags ALL ALL -j DROP

# Drop incoming malformed XMAS packets
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-XMAX Pkts] "
iptables -A FILTERS -p tcp --tcp-flags SYN,FIN SYN,FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-XMAX Pkts] "
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
iptables -A FILTERS -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

#Drop all NULL packets
iptables -A INPUT -p tcp --tcp-flags ALL NONE -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-NULL Pkts] "
iptables -A FILTERS -p tcp --tcp-flags ALL NONE -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-NULL Pkts] "
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
iptables -A FILTERS -p tcp --tcp-flags ALL NONE -j DROP
iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
iptables -A FILTERS -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

#Drop FIN packet scans
iptables -A INPUT -p tcp --tcp-flags FIN,ACK FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-FIN Scan] "
iptables -A FILTERS -p tcp --tcp-flags FIN,ACK FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix "[IPT-FIN Scan] "
iptables -A INPUT -p tcp --tcp-flags FIN,ACK FIN -j DROP
iptables -A FILTERS -p tcp --tcp-flags FIN,ACK FIN -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
iptables -A FILTERS -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

#Log and drop broadcast /multicast and invalid
iptables -A INPUT -m pkttype --pkt-type broadcast -j LOG --log-prefix "[IPT-Broadcast] "
iptables -A INPUT -m pkttype --pkt-type broadcast -j DROP
iptables -A INPUT -m pkttype --pkt-type multicast -j LOG --log-prefix "i[IPT-Multicast] "
iptables -A INPUT -m pkttype --pkt-type multicast -j DROP
iptables -A INPUT -m state --state INVALID -j LOG --log-prefix "[IPT-Invalid] "
iptables -A FILTERS -m state --state INVALID -j LOG --log-prefix "[IPT-Invalid] "
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A FILTERS -m state --state INVALID -j DROP

# The packets having the TCP flags activated are dropped
# and so for the ones with no flag at all (often used with Nmap scans)
iptables -A FORWARD -p tcp --tcp-flags ALL ALL -j log-and-drop
iptables -A FILTERS -p tcp --tcp-flags ALL ALL -j log-and-drop
iptables -A FORWARD -p tcp --tcp-flags ALL NONE -j log-and-drop
iptables -A FILTERS -p tcp --tcp-flags ALL NONE -j log-and-drop

#Allow everything from 127.0.0.1
iptables -t filter -A INPUT -j ACCEPT --src 127.0.0.1

#Allow everything from $ManagementIP
iptables -t filter -A INPUT -j ACCEPT --src "$ManagementIP"

#Allow only VPN interface addresses access to PI-Hole admin web interface
#iptables -t filter -A INPUT -j ACCEPT --protocol tcp --dport 80 --src "$ManagementIP"

#Allow SSH on port 22
iptables -t filter -A INPUT -j ACCEPT --protocol tcp --dport 22

#Allow Wirehuard VPN on UDP 51820
iptables -t filter -A INPUT -j ACCEPT --protocol udp --dport 51820

#NAT for wireguard VPN
iptables -t nat -A POSTROUTING -s 10.7.0.0/24 ! -d 10.7.0.0/24 -j SNAT --to "$LocalIP"
iptables -I INPUT -p udp --dport 51820 -j ACCEPT
iptables -I FORWARD -s 10.7.0.0/24 -j ACCEPT
iptables -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

echo "end of services"

# allow ping at 2 per sec
iptables -t filter -A INPUT -j ACCEPT --in-interface $INET_IF --protocol icmp --icmp-type echo-request --match limit --limit 4/s --limit-burst 3
iptables -t filter -A INPUT -j log-and-drop  --in-interface $INET_IF --protocol icmp --icmp-type echo-request

# allow responces to local initated connections
#iptables -A INPUT -i  $INET_IF --match state --state NEW,INVALID -j log-and-drop
#iptables -A FORWARD -i $INET_IF  --match state --state NEW,INVALID -j log-and-drop
iptables -t filter -A INPUT -j ACCEPT --match state --state RELATED,ESTABLISHED
 
# Set rp_filter to 2
for i in `find /proc/sys/net/ipv*/conf -name rp_filter`
do
	echo "2" >$i
done
 
# setup a default deny rule for outside traffic
iptables -t filter -A INPUT --in-interface $INET_IF -j log-and-drop
iptables -t filter -A FILTERS --in-interface $INET_IF -j log-and-drop
   
systemctl restart fail2ban
echo "Restarting fail2ban"

exit 0

6. Set the executable permissions on the /etc/rc.local script:

chmod a+x /etc/rc.local

7. Ensure it is run by systemd at startup:

systemctl enable rc-local

8. Edit the /etc/rc.local script, making the following changes:

  • Change the INET_IF to match the public interface name of your system
  • Change the IP address in LocalIP to that of the public IP of your system

9. Since our /etc/rc.local script sets the rules needed for Wireguard, disable the Wireguard Road Warrior iptable rules installer:

systemctl stop wg-iptables
systemctl disable wg-iptables
○ wg-iptables.service
     Loaded: loaded (/etc/systemd/system/wg-iptables.service; disabled; preset: enabled)
     Active: inactive (dead)

Disable UFW

10. If UFW is installed, disable it:

ufw disable
Firewall stopped and disabled on system startup
ufw status
Status: inactive

Reboot

11. Reboot

Final Steps

12. Once the system reboots, login to the VM and ensure that the rc.local script has run with:

systemctl status rc-local
● rc-local.service - /etc/rc.local Compatibility
     Loaded: loaded (/lib/systemd/system/rc-local.service; enabled-runtime; preset: enabled)
    Drop-In: /usr/lib/systemd/system/rc-local.service.d
             └─debian.conf
     Active: active (exited) since Mon 2024-06-03 05:39:16 UTC; 1 week 3 days ago
       Docs: man:systemd-rc-local-generator(8)
    Process: 1719 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)
        CPU: 100ms

Jun 03 05:39:16 test-server-1 rc.local[1800]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1802]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1808]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1810]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1812]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1814]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1719]: end of services
Jun 03 05:39:16 test-server-1 rc.local[1828]: iptables: No chain/target/match by that name.
Jun 03 05:39:16 test-server-1 rc.local[1719]: Restarting fail2ban
Jun 03 05:39:16 test-server-1 systemd[1]: Started rc-local.service - /etc/rc.local Compatibility.

13. Check to ensure the iptables rules for Wireguard were installed:

iptables -L -n -v -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    3  1485 SNAT       0    --  *      *       10.7.0.0/24         !10.7.0.0/24          to:<LocalIP>

14. Check the status of Wireguard to make sure it is running:

systemctl status wg-quick@wg0
● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
     Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; preset: enabled)
     Active: active (exited) since Mon 2024-06-03 05:31:25 UTC; 1 week 3 days ago
       Docs: man:wg-quick(8)
             man:wg(8)
             https://www.wireguard.com/
             https://www.wireguard.com/quickstart/
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
             https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
    Process: 839 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
   Main PID: 839 (code=exited, status=0/SUCCESS)
        CPU: 47ms

Jun 03 05:31:25 test-server-1 systemd[1]: Starting wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0...
Jun 03 05:31:25 test-server-1 wg-quick[839]: [#] ip link add wg0 type wireguard
Jun 03 05:31:25 test-server-1 wg-quick[839]: [#] wg setconf wg0 /dev/fd/63
Jun 03 05:31:25 test-server-1 wg-quick[839]: [#] ip -4 address add 10.7.0.1/24 dev wg0
Jun 03 05:31:25 test-server-1 wg-quick[839]: [#] ip link set mtu 1420 up dev wg0
Jun 03 05:31:25 test-server-1 systemd[1]: Finished wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0.

15. Turn off the PIHole admin interface password since you can only access it via the VPN

pihole -a -p

16. You are now ready to connect to the VPN using your client. Once connected, open a web browser and navigate to http://pi.hole to open to the admin interface.