This guide is suited for the security exigences of a home network and for private use; in a business environment, especially if you deal with customer’s sensible data, is strongly advised to use a professional device that uses pfsense firewall software and strong VPN encryption; NetGate for instance offers a wide variety of physical devices and cloud solutions that fit security exigences and traffic loads from remote working and small business to large offices, corporate business and data centers.
- install and configure on the Raspberry PI (RPI) a VPN server using Wireguard protocol;
- install and configure on the RPI a network-wide DNS sinkhole for blocking ads, tracking, scam, malware and phishing known referrals using Pi Hole;
- configure the RPI to be used as gateway, to block IPv6 traffic for security purposes and hijack hard-coded DNS providers on Smart-TVs;
- cover every aspects of RPI environment program-wise, from updates to backup and automated checks of running instances, so once you finished programming you can forget to check if its working.
- configure unattended-upgrades to automate Raspberry Pi OS updates;
- configure the RPI to refuse SSH password access and use instead a security key;
- setup NAT and firewall rules for security purposes and preventing DoT and DoH queries;
- configure a DDNS service to access the VPN server through the dynamic public IP address given by your ISP from smartphones, tablets and laptops while are not connected to the LAN;
- configure a VPS service to bypass CGNAT or NAT2 to access your RPI VPN server when your smartphones, tablets and laptops are not connected to the LAN;
- implement a diagnostic script that checks that services intalled on RPI are working propely;
- implement a watchdog timer that regularly checks VPN server and Pi Hole status;
- implement a script to automate the creation and purge of VPN clients, their relative access keys and and configuration files,that also shows a configuration QR code for smartphones and tablets;
- automate the creation of a password protected backup archive of RPI configuration and its upload on a cloud storage service;
- implement a script for manual restore from a password protected backup archive file.
Hardware used is a RPI 4 with 4Gb RAM and 64Gb microSD memory card, cable connected to my 5G modem/router LAN port, but it can be set to use Wi-Fi connection instead. You’ll also need a microSD card reader.
The operative system installed on the RPI is Raspberry Pi OS 64bit headless (without desktop environment), based on Linux Debian Bookworm.
Required additional Linux software packages from Debian APT: unattended-upgrades, bsd-mailx, nftables, fail2ban, wireguard, qrencode, rclone, ddclient, zip, unzip.
Required additional software from external source: Pi-Hole.
PC used for programming client-side uses Arch Linux OS.
Raspberry Pi Imager sofware for your preferred OS can be downloaded from Raspberry official website; for Arch Linux can be installed directly from pacman as is part of extra repository.
Choose your RPI model, the desired version of Raspberry Pi OS and the microSD card of destination.
After clicking NEXT button, edit the configuration and enable SSH service otherwise you will not have access to the RPI if you have chosen an headless OS; change the default userID (pi), set an access secure password, locales and keyboard configuration; also setup SSID name, access credentials and country in case you want to connect to the RPI via Wi-Fi.
The imager will format your microSD card and install selected OS; a message will pop-up after the procedure is finished, telling to remove the microSD card from the reader.
Insert the microSD card and power up the RPI.
To access the RPI via SSH you need to provide userID, RPI IP address or localhost name and password in a command via terminal that follow this syntax:
ssh userID@192.168.XXX.XXXYou can discover the IP address assigned by router’s DHCP to the RPI by accessing to your router web-admin page or running this command on your Linux client:
ip neigh showor running this other command if you have nmap installed:
nmap -sP 192.168.0.0/16NOTE: following this guide you will set a static IP through the RPI configuration, but is always a good practice to reserve a specific static IP address in router’s configuration: it will link the RPI connected device’s MAC address to the specified LAN IP address. It should be done for all the clients that will use the RPI for a better control over your network. You can also adjust the DHCP range of your router.
Once access is gained with password set in Raspberry Pi Imager configuration, execute:
sudo raspi-configan interactive menu will pop-up; choose “Advanced Settings” (last option on the list) and then “Expand File System”.
You can navigate menu with ARROW keys, TAB key and confirm with ENTER key.
Exit the menu and the RPI will reboot to expand the file system to the whole microSD card.
The connection via SSH from your client terminal to the RPI will be terminated, obviously.
After rebooting, access again the RPI via SSH and update the RPI executing:
sudo apt update && sudo apt upgrade -yIt will take some time, depending on the amount of the upgrades needed by the system and your internet connection speed.
After upgrade process has finished, execute this command:
sudo nmtuifrom the interactive menu that will show up you will be able to select the connection device you’re using (ethernet or Wi-Fi) and set up a static IP address, gateway and DNS server(s) address(es). If, as suggested, you reserved a static IP address on the router, be sure that the IP address set is the same.
Reboot the RPI and all configuration changes will take effect:
sudo rebootUnattended-upgrades is a Debian software package that automate the download and install of available updates, including the OS version upgrades when released, reboot the system when is required from the updates and auto clean the system from unused software packages, dependecies and old kernels;
it needs to be installed, configured to match RPI architecture and activated;
access the RPI via SSH and execute:
sudo apt install -y unattended-upgrades bsd-mailxafter the installation process is completed, edit the configuration file:
sudo nano /etc/apt/apt.conf.d/50unattended-upgradeslook for the file section that starts with
Unattended-Upgrade::Origins-Pattern
and after last line that starts with
"origin=Debian,codename=…
add the two following lines to include RPI architecture to update sources:
"origin=Raspbian,codename=${distro_codename},label=Raspbian";
"origin=Raspberry Pi Foundation,codename=${distro_codename},label=Raspberry Pi Foundation";scroll down to the section
//Send mail to this address…
and set your internal mail address on this line; if its commented with // symbols uncomment it otherwise the command will be ignored:
Unattended-Upgrade::Mail "userID@localhost";
set these other lines to match following configuration and uncomment it:
system will send you and internal mail in case of an update error, will automatically remove unused packages, dependencies and old kernels and reboot the system when is required by the update process:
Unattended-Upgrade::MailReport "only-on-error";
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
save file and exit nano editor;
now you need to perform a “dry run” of unattended-upgrades to check that changes in configuration file are set properly:
sudo unattended-upgrades -d -v --dry-runand if you’re not getting any error messages you can enable unattended-upgrade process:
sudo dpkg-reconfigure --priority=low unattended-upgradesSecurity key implements strong security standards to SSH access to your RPI and along with failtoban and firewall rules that will be configured later prevent access through brute-force attacks on your RPI SSH port; for this purpose you can also change the default port (22) used by SSH protocol.
First you need to generate the security key on your Linux client PC:
ssh-keygen -t rsayou will be asked to give a name to generated security key;
both public and private key will be stored in /home/client_userID/.ssh/ directory of your Linux client PC;
copy the public key to the RPI, adjusting keyname, RPI_static_IP and userID varibles according to your settings:
scp ~/.ssh/keyname_rsa.pub userID@RPI_static_IP:/home/userID/now you need to access to the RPI via SSH and create a file that SSH daemon will look for when you’re trying to access the RPI with the security key:
install -d -m 700 ~/.sshnow you can add the public key to the created authorized_keys file:
cat /home/$(logname)/keyname_rsa.pub >> ~/.ssh/authorized_keysand then set the correct permission, user and group to the file:
sudo chmod 644 ~/.ssh/authorized_keys
sudo chown $(logname):$(logname) ~/.ssh/authorized_keysyou need to repeat the steps regarding key creation, copy public key to the RPI and add it to authorized_key file procedures from all the PCs and laptops you wish to grant SSH key access to your RPI;
you can also remove the public key file:
rm /home/$(logname)/keyname_rsa.pubafter all PCs and laptops keys has been added, you need to edit the SSH configuration file to prevent access via password:
sudo nano /etc/ssh/sshd_configlook for the line PasswordAuthentication, if is commented with # symbol uncomment it and set bolean value to “no”.
Save file and exit nano editor (CTRL+O, ENTER, CTRL+X).
After rebooting the RPI you will need to call the security key to access the RPI via SSH:
ssh -i /home/$(logname)/.ssh/keyname_rsa userID@RPI_static_IPSSH security keys can be also generated from MacOS, Windows and other operating systems, I’ll leave you the pleasure to do a simple web search to get this knowledge.
The installation process is completely automated and you will be asked only few simple questions to complete the setup and get the Pi Hole working:
curl -sSL https://install.pi-hole.net | bashdon’t forget to take note of the web-admin page password that will be shown at the end of installation process and to refer to Pi Hole official website for documentation, commands, customization and eventual issues.
To be able to operate, Pi Hole needs blocklists and eventually whitelists of domains; a simple web search will be enough to find many.
NOTE: As YouTube and Twitch serve their ads through their main domains, Pi Hole will NOT be able to block it.
Now you can set up two cron jobs to automate the update of the Pi Hole and the Pi Hole blocklists (Gravity):
sudo crontab -eif this is your first access to crontab you will be asked to choose an editor from a list;
add this two lines that you can customize to fit your preference:
the first line sets the blocklists update at 4:00 AM every 3rd day of the week;
second line sets Pi Hole update at 5:00 AM every 3rd day of the week;
0 4 * * 3 /bin/bash /home/userID/ya-pihole-list/adlists-updater.sh 1 >/dev/null
0 5 * * 3 /usr/bin/date >> /var/log/pihole_update.log && /usr/local/bin/pihole -up >> /var/log/pihole>save the crontab file and exit editor;
now you can reboot the RPI and all changes will take effect;
you need to set your network clients to use RPI_static_IP as DNS address or you can set it as DNS address directly on your router settings so it will be used network-wide.
This script will provide the installation of necessary software packages and the configuration of various services like Wireguard VPN server, disable IPv6 traffic for security purposes, sets nftables rules to use the RPI as gateway and hijack hard-coded DNS providers in Smart-TV, sets firewall and failtoban rules and configures ddclient to access your VPN server while your smartphones or laptops are not connected to the LAN if your ISP gives you a dynamic IP address and your line is not under CGNAT.
While is quite simple to disable IPv6 traffic through the configuration of the network manager on your PC or laptop, and also many Smart-TV models give the oprion in their network settings, is way more difficult on your smartphone because it will probably require root privileges; setting the RPI as your gateway while your smartphone is connected to the LAN or VPN will block all IPv6 traffic.
You need to forward the udc port you will use for your VPN server (51234 in this example) from the exterior to your RPI_static_IP in your router configuration; if your router does not have port forwarding function, it will probably have virtual server function where you can set the same rule.
NOTE: the VPN you are installing will NOT hide your public IP address; it will only encrypt the communications from your device to the destination you're reaching, avoiding third parties to be able to intercept your data. To hide your public IP or de-geolocalize it for purposes like see Netflix content not available in your country, you'll need a commercial VPN subscription, that gives you the option to connect to servers located in different countries; there's a wide variety of offers in the VPN market, but providers that are unanimously considered the best ones privacy-wise are swedish Mullvad and swiss Proton due to the strict privacy laws of coutries they're operating from.
With rules set in nftables and previous configuration of SSH access only with security key, SSH port is already protected from brute-force attacks and is also not exposed to WAN direct access, but can be only reached from LAN or VPN addresses; fail2ban is installed only for auditing/forensic purposes on failed access logs, but will be useful if you will change rule settings on SSH port.
For ddclient configuration, you will need some parameters that can be obtained from the control panel of DDNS provider service you subscribed, like protocol used, username, password and third level domain you have chosen.
If you have a static public IP you can skip ddclient configuration with CTRL+C.
From the home directory of RPI (home/userID/) copy the following command and paste it in the terminal; it will download the install_services.sh script to your home directory:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/install_services.shif you don't want to use default variables value edit the script with nano:
sudo nano install_services.shmake the file executable:
sudo chmod +x install_services.shand execute the script to install and configure services:
sudo ./install_services.shafter the script has finished services installaion you need to reboot the RPI.
ISP implements security features on the internet line you subscribed for and most common are CGNAT and double NAT or NAT2, that are used when it gives you a dynamic public IP address.
With those features configured on your internet line you will be unable to access the RPI from the WAN and consequentially you will be also unable to use your Wireguard VPN when you're not connected to LAN.
The following shell script will help you to check your internet line and know if you are under CGNAT or NAT2;
From the home directory of RPI (home/userID/) copy the following command and paste it in the terminal; it will download the cgnat_check.sh script to your home directory:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/cgnat_check.shmake the script executable:
chmod +x cgnat_check.shand run the scropt with:
sudo ./cgnat_check.shIf you want also to check the correct forwarding of VPN/DDNS udp port (51234) from WAN to your RPI you can use YouGetSignal web tool.
In case you're under CGNAT or NAT2 you can check with your ISP the possibility to change from dynamic to static public IP address; this will probably involve some fees.
If your ISP configured a NAT2 on your line it probably offers the function of port forwarding through a control panel or upon request.
If you are under CGNAT and your ISP can't give you a static public IP address, if you are under NAT2 and your ISP don't allow port forwarding function and if you can't or don't want to change to a different ISP that offers those options, there's a workaround: you can subscribe for an online Linux VPS service; with a web search you can find many offers on the VPS market, and also a lot of scams, so choose your VPS service provider wisely; there are some free solution even from tech colossus like Google and Oracle. Paid services for the specs you'll need will cost you € 1,00 per month.
VPS online server will give you a static IP address; you can install wireguard on VPS, with a different subnet from one you have installed on RPI, to act as server and install a client on RPI that will automatically connect to the VPS creating an encrypted tunnel connection; then you can route all traffic from VPS to RPI. In this way you can use your smartphone, tablet or laptop, configured as client of RPI VPN server that you set up in previous chapter, to use the VPS static public IP address to connect to the RPI and activate their VPN tunnels, bypassing CGNAT or NAT2 from your ISP.
Most, if not all, VPS server are configured to accept SSH only with security key given when you open an account, to prevent brute-force attacks. Access your VPS server via SSH and execute this command to check if wget is installed; if sudo command doesn't work due to VPS configuration, gain root privileges executing command "su", and remove "sudo" part in following commands:
sudo apt update && sudo apt install -y wgetDownload the VPS_server.sh script; it will install Wireguard software, configure a virtual device called "wg_cgnat" that listen over udp port "51234", configure the VPS server as VPN server and forward traffic to RPI client that will be configured later, configure firewall rules to block all incoming traffic except over udp 51234 port and tcp 22 port (SSH) and configure fail2ban to block SSH access tries with password:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/VPS_server.shmake the script executable:
sudo chmod +x VPS_server.shand run the scropt with:
sudo ./VPS_server.shonce the script finished the installation and configuration process, copy the VPS public key shown to add it to VPS client configuration.
Now, log in to your RPI and download the VPS_client.sh script; it will configure the virtual device "wg_cgnat" and add the configuration to RPI to act as a client of VPS server and receive forwarded traffic;
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/VPS_client.shedit the script, add the VPS public key and the VPS static public IP address:
sudo nano VPS_client.shsave file, exit editor and make the script executable:
sudo chmod +x VPS_client.shrun the scropt with:
sudo ./VPS_client.shwhen script finished the configuration process, copy RPI public key shown and add it to wg_cgnat.conf file on VPS server.
Access VPS server via SSH and edit configuration file:
sudo nano /etc/wireguard/wg_cgnat.confpaste RPI public key from RPI client configuration and reboot the VPS server to make changes effective.
Back to tour RPI edit nftables configuration file:
sudo nano /etc/nftables.conflook for this section of the script:
table inet filter {
chain input {
and after following lines:
iif "lo" accept
ct state established,related accept
and add following rule to accept connection coming from your VPS server:
iif "wg_cgnat" ip saddr 10.100.100.1 acceptSave file, exit editor and restart the RPI to make all changes effective.
This shell script will act as VPN client manager, giving you the options to:
- create a new VPN peer looking for first available VPN address, generate access keys and configuration file and display a configuration QR code that can be red from smartphones and tablets using official Wireguard app for Android or iOS;
- delete an existing peer with its access keys and configuration file, typing its name from a displayed list of configured peers.
- if your internet line has a static public IP address and the VPN UDP port is forwarded by router settings, set as ENDPOINT your static public IP address;
- if your internet line has a dynamic public IP address but its NOT under CGNAT, your ISP allowed VPN UDP port forwarding if its under NAT2, the VPN UDP port is forwarded by router settings and you configured ddclient, you should set as ENDPOINT the third level domain you got from your DDNS service;
- if you have configured a VPN on VPS that forwards traffic to the the RPI as its client, you should set as ENDPOINT the VPS static public IP address or domain;
- otherwise set as ENDPOINT the RPI_static_IP, but it will work ONLY when clients are connected to LAN.
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/wg_client_manager.shopen the script with nano and change the variables in the beginning of the file to match your settings:
sudo nano wg_client_manager.shsave the file and exit nano;
make the file executable and move it to a more appropriate directory:
sudo chmod +x wg_client_manager.sh
sudo mv wg_client_manager.sh /usr/local/bin/run the script with:
sudo wg_client_manager.shTo use the VPN connection from your Linux client PC you need to install Wireguard software (wireguard-tools) and copy the VPN client configuration file from /etc/wireguard/clients/ folder of your RPI; is advised that you have also a resolver (openresolv or systemdresolved) installed on your system.
Various linux connection managers, like systemd-networkd, netctl, NetworkManager and ConnMan, supports wireguard protocol, so you can refer to their user manuals about how to import the configuration file or manually setup the VPN tunnel with data included in the file; you will need to set these parameters:
- virtual interface name - corresponds to name you set for the VPN cilent, and name given to configuration file;
- client private key - included in configuration file;
- nodes / VPN server public key - included in configuration file;
- nodes / allowed IPs - included in configuration file;
- VPN endpoint and listening port - included in configuration file;
- VPN client IP address - assigned during client creation (i.g.: 10.8.0.X);
- VPN netmask - 24 (255.255.255.0);
- VPN gateway IP address - 10.8.0.1;
- DNS server IP address - RPI static IP address (i.g.: 192.168.XXX.XXX);
This script, when launched, will check that services you installed are working properly.
change variables in the beginnig of file according to your settings;
From the home directory of RPI (home/userID/) copy the following command and paste it in the terminal; it will download the diagnostic.sh script to your home directory:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/diagnostic.shif you did not used default variables value, open the script with nano and change the values in the beginning of the file to match your settings:
sudo nano diagnostic.shmake the file executable and move it to a more appropriate directory:
sudo chmod +x diagnostic.sh
sudo mv diagnostic.sh /usr/local/bin/run the script with:
sudo diagnostic.shWatchdog timer is a useful service that regularly checks the operational status of the VPN server and Pi Hole, and restore it in case of failure.
From the home directory of RPI (home/userID/) copy the following command and paste it in the terminal; it will download the watchdog.sh script, pi-vpn-watchdog.service and pi-vpn-watchdog.timer ini files to your home directory:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/watchdog.sh
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/pi-vpn-watchdog.service
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/pi-vpn-watchdog.timerIn case you installed the VPS CGNAT bypass, edit the script and uncomment last 4 lines, it will also check virtual device "wg_cgnat".
Make the script watchdog.sh executable and move it to the appropriate directory:
sudo chmod +x watchdog.sh
sudo mv watchdog.sh /usr/local/bin/move the two ini files to the appropriate directory:
sudo mv pi-vpn-watchdog.service /etc/systemd/system/
sudo mv pi-vpn-watchdog.timer /etc/systemd/system/Now you need to reload daemon and enable the new service so it will be automatically loaded on every RPI boot:
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl enable pi-vpn-watchdog.timer
sudo systemctl start pi-vpn-watchdog.timerThis script will perform a backup of all settings you configured following this guide including: unattended-upgrades, SSH access public key, nftables rules, fail2ban, Wireguard (configuration, clients and keys), Pi Hole (configuration and blocklists), watchdog timer, ddclient, rclone and all custom scripts you stored in /usr/local/bin/ folder of your RPI; then will create an encrypted archive and upload it to your favorite cloud storage service using rclone software.
As configuration process may differ from one storage service to another, pleae refer to rclone manual to configure the software and setup your storage service to accept your archive file (you probably need to enable some APIs).
You can start the configuration process launching:
rclone configAfter configuration process has finished, if you need to launch it again to change some parameters and previous command gives you errors, execute:
sudo rclone config --config /home/$(logname)/.config/rclone/rclone.confThe backup script is fully automated, you just need to set as variable the name of the folder you configured on your cloud storage drive where backup files will be uploaded.
During first run you will be asked to set a password to encrypt the backup archives; it will be stored in the system and file permission will be changed so only root will be able to read it; take a note of the password anyway.
The script has also a function to delete from your cloud storage backup files older thant 15 days.
Copy this command to download the backup.sh script to your RPI home folder:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/backup.shedit the script with nano and set cloud storage folder name varible according to your settings:
sudo nano backup.shsave file and exit nano;
make the script executable and move it to the appropriate directory:
sudo chmod +x backup.sh
sudo mv backup.sh /usr/local/bin/run the script with:
sudo backup.shYou can set a cron job to automatically launch the backup once a week: open crontab;
sudo crontab -eadd this line to execute backup process at 4 AM every 6th day of the week (saturday):
0 4 * * 6 /usr/local/bin/backup.shsave crontab file and exit editor; reboot the RPI to make changes to crontab effective.
This restore script will help you to restore the configrations you set in previous chapters of this guide. You can choose from a full system restore to the restore of a single feature, like wireguard configuration or nftables configuration. It will also ask you if you want to restore from a cloud saved backup file directly with rclone or providing a link to the cloud backup archive, or from a local backup file from home folder of the RPI.
If the script will not find the decrypting backup archive password it will automatically start a full restore.
In case of a full restore, that will be necessary if you re-install the Raspberry Pi OS, you just need to expand the file system on the microSD card and set the RPI static IP address with nmtui command like explained in chapters 2 and 3 of the guide, and restore script will do all the rest, including setting the IP forward, block IPv6 protocol, seting SSH acces with your security key, restoring your cron jobs and enable daemon services.
After the restoring process is complete it will also ask if you want to clean files in temporary folder used for the process and restart the RPI to make all changes effective.
Copy this command to download the restore.sh script to your RPI home folder:
wget https://github.com/Ale888elA/Pi-Hole-VPN-gateway/raw/main/scripts/restore.shmake the script executable and move it to the appropriate directory:
sudo chmod +x restore.sh
sudo mv restore.sh /usr/local/bin/run the script with:
sudo restore.sh