WireGuard DoV (DNS-over-VPN)
xeptore

xeptore @xeptore

Joined:
Jun 28, 2025

WireGuard DoV (DNS-over-VPN)

Publish Date: Jun 29
1 1

Why?

To preserve DNS privacy, and prevent sniffing/spoofing, which is a common technique used by ISPs to restrict internet access in countries like mine, we can tunnel DNS traffic through WireGuard and use a remote, private, and trusted DNS server. Keep in my that this will only tunnel DNS traffic.

Setup

In this guide, I'll use Adguard dnsproxy as the DNS resolver, which is easy to setup and get it to work. Of course you can use any DNS server of your choice. Some alternatives that I can remember are (in no specific order):

WireGuard Server Config

# wgdns.conf
[Interface]
PrivateKey = server_prv
Address = 10.3.3.1/24
Address = 10.0.0.1/32
ListenPort = 64539
MTU = 1280

[Peer]
PublicKey = client_pub
PresharedKey = psk
AllowedIPs = 10.3.3.2/32
Enter fullscreen mode Exit fullscreen mode

Subnet 10.3.3.0/24 is used for WireGuard peers, and 10.0.0.1/32 will be assigned to the DNS server.

WireGuard Client Config

[Interface]
PrivateKey = client_prv
Address = 10.3.3.2/32
MTU = 1280
DNS = 10.0.0.1

[Peer]
PublicKey = server_pub
PresharedKey = psk
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 15
Endpoint = server_ip:64539
Enter fullscreen mode Exit fullscreen mode

dnsproxy

Download and extract the latest release of dnsproxy, and spin it up:

./dnsproxy \
  --cache \
  --ipv6-disabled \
  --pending-requests-enabled \
  --refuse-any \
  --udp-buf-size=4096 \
  -l 10.0.0.1 \
  -p 53 \
  -u udp://1.1.1.1:53 \
  -u udp://1.0.0.1:53
Enter fullscreen mode Exit fullscreen mode

Or with equivalent config file:

# config.yaml
cache: true
ipv6-disabled: true
pending-requests-enabled: true
refuse-any: true
udp-buf-size: 4096
listen-addrs:
  - 10.0.0.1
listen-ports:
  - 53
upstream:
  - udp://1.1.1.1:53
  - udp://1.0.0.1:53
Enter fullscreen mode Exit fullscreen mode

Firewall Rules

  • Allow WireGuard inbound connections:

    ufw rule allow in on eth0 proto udp to SERVER_PUBLIC_IP/32 port 64539 comment 'WireGuard Inbound'
    

    Where eth0 is the public-facing network interface of the server.

  • Allow WireGuard peers to query DNS server:

    ufw rule allow in on wgdns proto udp from 10.3.3.0/24 to 10.0.0.0/24 port 53 comment 'WireGuard DNS'
    

    Where wgdns is the name of WireGuard interface.

Limitations

Spinning up the dnsproxy server(s) should happen after WireGuard interface is up as IP(s) are not available otherwise. One solution for having a separate DNS interface is to use the dummy network kernel module like the following:

ip link add dummy0 type dummy
ip addr add 10.0.0.1/32 dev dummy0
ip link set dummy0 mtu 1280 up
Enter fullscreen mode Exit fullscreen mode

If dummy module isn't already enabled:

modprobe dummy
Enter fullscreen mode Exit fullscreen mode

Note: This is not persistent against reboots. Ask ChatGPT for ways to make it so.

Moving Forward

  • You can spin up multiple dnsproxy servers on different IPs. In order for peers to access it, you'll need to:

    • Assign a new IP to the DNS interface (wgdns or dummy0)
    • Apply the respective firewall rule
    • Update both DNS, and AllowedIPs of peers to match the new DNS server IP
  • You can use port-forwarded UDP, or TCP (DoH, or DoT for example) ports instead of local process to escape network restrictions (e.g., by using tools like rathole)

Bonus

dnsproxy Systemd service template: https://github.com/xeptore/gist/blob/main/systemd/dnsproxy@.service

Comments 1 total

Add comment