Gabriel Cachadiña

WireGuard Server with Pi-Hole

· Gabriel Cachadiña

In my quest to achieve the most secure internet connection possible, I configured my local server so that all my traffic is tunneled through WireGuard to my server, and that any traffic wishing to access the internet must also pass through Pi-Hole to get an ad-free* and more secure internet experience.

What is WireGuard?

WireGuard is a modern VPN protocol that allows encrypted tunnels to be established between peers. WireGuard_VPN WireGuard offers the following advantages1 compared to other VPN servers:

Configuration

A WireGuard communication setup must consist of both a server and a client.

WireGuard Server

To configure a WireGuard server, the following steps are required:

  1. Enable port 51820 in the firewall of both the router and the server. Only UDP traffic needs to be allowed.
  2. Configure a NAT rule so that traffic from the network interface being used (for example enp1s0) is routed through an internal network interface that will conventionally be named wg0.
  3. Configure the server keys, which will be used to encrypt client traffic. To generate these keys, use the following commands:
1wg genkey > private.key
2wg pubkey < private.key > public.key
  1. Define the IP address of the wg0 interface, the server’s private key, and the public keys of the devices that will connect to it. Additionally, define an IP address or range of IP addresses that these devices will use within the wg0 network.

Below is an example configuration in NixOS:

 1{
 2  # enable NAT
 3  networking.nat.enable = true;
 4  networking.nat.externalInterface = "enp1s0";
 5  networking.nat.internalInterfaces = [ "wg0" ];
 6  networking.firewall.trustedInterfaces = [ "wg0" ];
 7  networking.firewall.allowedUDPPorts = [ 51820 ];
 8
 9  networking.wireguard.interfaces = {
10    wg0 = {
11      ips = [ "10.100.0.1/24" ];
12      listenPort = 51820;
13
14      privateKey = "${config.globals.wireguard_server_privatekey}";
15      peers = [
16        { # TFN
17          publicKey = "${config.globals.wireguard_tfn_publickey}";
18          allowedIPs = [ "10.100.0.2/32" ];
19        }
20        { # NomadUSB
21          publicKey = "${config.globals.wireguard_nomadusb_publickey}";
22          allowedIPs = [ "10.100.0.3/32" ];
23        }
24      ];
25    };
26  };
27}

WireGuard Client

A private/public key pair must be generated, and the public key must be shared with the server you want to connect to.

To configure a client connection, the following parameters must be added:

Pi-Hole

Pi-hole is a utility that creates a DNS server capable of caching visited domains and restricting access to them. Pi-Hole is very useful if, for security reasons, you want to block:

Note that Pi-Hole does not replace a firewall or an IDS system, but it significantly reduces exposure to trackers and malicious domains.

Installation

To install Pi-Hole, I strongly recommend using docker compose and the host network mode. In NixOS, the configuration would look like this:

 1{ config, pkgs, ... }:
 2
 3{
 4  virtualisation.docker.enable = true;
 5  virtualisation.oci-containers = {
 6    backend = "docker";
 7    containers.pihole = {
 8      image = "pihole/pihole:latest";
 9      environment = {
10        TZ = "Europe/Madrid";
11        PUID = "1000";
12        PGID = "1000";
13        FTLCONF_webserver_api_password = "${config.globals.pihole_password}";
14      };
15      volumes = [
16        "/home/gabriel/Docker/Pihole/etc-pihole:/etc/pihole"
17        "/home/gabriel/Docker/Pihole/etc-dnsmasq.d:/etc/dnsmasq.d"
18      ];
19      ports = [
20        "53:53/tcp"
21        "53:53/udp"
22        "67:67/udp"
23        "80:80/tcp"
24      ];
25      networks = [ "host" ];
26      autoStart = true;
27    };
28  };
29  networking.firewall.allowedTCPPorts = [ 80 ];
30}

Pi-Hole Configuration

With the program installed using the script above, port 53 will act as the DNS server. You will need to configure the Pi-Hole domains. Note that in the configuration shown earlier, ports 53, 67, and 80 are exposed to the outside of the machine, meaning any user could access them. In my case, this was intentional because I want other users to be able to use this proxy to secure their traffic. However, if Pi-Hole is only going to be used to protect WireGuard traffic, I recommend using 127.0.0.1:xx:xx so that Pi-Hole is accessible only from the server itself.2

Pi-Hole and WireGuard

Finally, for WireGuard to use Pi-Hole, you must configure the client to use the DNS option 10.100.0.1. It would be possible to configure Docker to use this DNS server by default, but that could cause an issue: if Pi-Hole crashes, we would lose access to WireGuard as well. Final Traffic Flow


  1. I strongly recommend reading the official page and reviewing the protocol’s advantages in more detail in the following video↩︎

  2. I would also like to clarify that all of this is within my local network, so these ports are not exposed to the internet. If they were, some form of rate limiting should be implemented on the DNS service to prevent a possible DDoS attack. ↩︎

#self-hosted #gnu/linux

Reply to this post by email ↪