Setting Up DoH on Raspberry Pi
In today's digital age, ensuring the security and privacy of our online activities is paramount. One effective way to achieve this is through DNS-over-HTTPS (DoH) technology, especially when implemented on a Raspberry Pi, a versatile and affordable platform. This article explores the steps to set up DoH on your Raspberry Pi, leveraging its capabilities to enhance your browsing security.
DNS-over-HTTPS encrypts DNS queries within the HTTPS protocol, making them resistant to eavesdropping and manipulation. By integrating DNS queries with regular web traffic, DoH ensures that your browsing habits remain private and protected from prying eyes.
This setup is tested on a raspberry pi (arm64) running debian 12 and acting as access point. For more details about my rpi setup, you can refer to this article:
Install dnscrypt-proxy
Start by installing the required software packages. Open a terminal on your Raspberry Pi and execute the following commands:
VERSION=2.1.5
wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/${VERSION}/dnscrypt-proxy-linux_arm64-${VERSION}.tar.gz
tar -xf dnscrypt-proxy-linux_arm64-${VERSION}.tar.gz
mv linux-arm64/ /opt/dnscrypt-proxy
rm -f dnscrypt-proxy-linux_arm64-${VERSION}.tar.gz
This will install the dnscrypt-proxy in /opt/dnscrypt-proxy
Configure dnscrypt-proxy
The setup already have few templates. As we will be using the proxy , we can start from example-dnscrypt-proxy.toml
cd /opt/dnscrypt-proxy
cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml
To begin the configuration, we'll start by editing the dnscrypt-proxy.toml file. The key settings to adjust include server_names, listen_addresses, bootstrap_resolvers and anonymized_dns.
server_names:
This variable will hold a list of servers from https://dnscrypt.info/public-servers
My best options are:
server_names = ['ams-doh-nl','ams-doh-nl-ipv6','ams-dnscrypt-nl','ams-dnscrypt-nl-ipv6']
You can choose any server, such as those that block ads and malware (e.g., dguard-dns-doh and ams-ads-doh-nl). I haven't used these because I have my own solution. For more information, you can refer to my article:
listen_addresses:
Since I'm using dnscrypt proxy alongside dnsmasq, and the latest is already operating on port 53, I will configure dnscrypt to use port 54 to avoid any port conflicts.
listen_addresses = ['127.0.0.1:54']
bootstrap_resolvers:
These are normal, non-encrypted DNS resolvers, that will be only used for one-shot queries when retrieving the initial resolvers list and if the system DNS configuration doesn't work. No user queries will ever be leaked through these resolvers, and they will not be used after IP addresses of DoH resolvers have been found (if you are using DoH).
By default, this list has quad9 DNS resolver and google . For better privacy, I've replaced google DNS with cloudflare DNS:
bootstrap_resolvers = ['9.9.9.11:53', '1.1.1.1:53']
anonymized_dns:
Routes are indirect ways to reach dnscrypt servers. A route maps a server name ("server_name") to one or more relays that will be used to connect to that server.
My best list was:
routes = [
{ server_name='ams-dnscrypt-nl', via=['anon-cs-dus4', 'anon-cs-dus3'] },
{ server_name='ams-dnscrypt-nl-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
{ server_name='ams-doh-nl', via=['anon-cs-ch2', 'anon-cs-berlin'] },
{ server_name='ams-doh-nl-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
]
You can get the relays from https://dnscrypt.info/public-servers as well and best to select the nearest possible location.
The final config file should be similar to:
server_names = ['ams-doh-nl','ams-doh-nl-ipv6','ams-dnscrypt-nl','ams-dnscrypt-nl-ipv6']
listen_addresses = ['127.0.0.1:54']
max_clients = 250
ipv4_servers = true
ipv6_servers = false
dnscrypt_servers = true
doh_servers = true
odoh_servers = false
require_dnssec = true
require_nolog = true
require_nofilter = true
disabled_server_names = []
force_tcp = false
http3 = false
timeout = 5000
keepalive = 30
cert_refresh_delay = 240
bootstrap_resolvers = ['9.9.9.11:53', '1.1.1.1:53']
ignore_system_dns = true
netprobe_timeout = 60
netprobe_address = '9.9.9.9:53'
log_files_max_size = 10
log_files_max_age = 7
log_files_max_backups = 1
block_ipv6 = false
block_unqualified = true
block_undelegated = true
reject_ttl = 10
cache = false
cache_size = 4096
cache_min_ttl = 2400
cache_max_ttl = 86400
cache_neg_min_ttl = 60
cache_neg_max_ttl = 600
[captive_portals]
[local_doh]
[query_log]
format = 'tsv'
[nx_log]
format = 'tsv'
[blocked_names]
[blocked_ips]
[allowed_names]
[allowed_ips]
[schedules]
[sources]
[sources.public-resolvers]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md']
cache_file = 'public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
[sources.relays]
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/relays.md']
cache_file = 'relays.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
[broken_implementations]
fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'cleanbrowsing-adult', 'cleanbrowsing-adult-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-security', 'cleanbrowsing-security-ipv6']
[doh_client_x509_auth]
[anonymized_dns]
routes = [
{ server_name='ams-dnscrypt-nl', via=['anon-cs-dus4', 'anon-cs-dus3'] },
{ server_name='ams-dnscrypt-nl-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
{ server_name='ams-doh-nl', via=['anon-cs-ch2', 'anon-cs-berlin'] },
{ server_name='ams-doh-nl-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
]
skip_incompatible = false
[dns64]
[static]
Install dnscrypt-proxy as service
After the configuration is done, it's time to install, enable and start the service:
./dnscrypt-proxy -service install
systemctl enable dnscrypt-proxy
systemctl start dnscrypt-proxy
Check the status of the service afterward to make sure it's up and running using:
systemctl status dnscrypt-proxy.service
You should get something similar to:
● dnscrypt-proxy.service - Encrypted/authenticated DNS proxy
Loaded: loaded (/etc/systemd/system/dnscrypt-proxy.service; enabled; preset: enabled)
Active: active (running) since Tue 2024-04-09 18:22:45 CEST; 1s ago
Main PID: 317798 (dnscrypt-proxy)
Tasks: 9 (limit: 9262)
CPU: 48ms
CGroup: /system.slice/dnscrypt-proxy.service
└─317798 /opt/dnscrypt-proxy/dnscrypt-proxy -config dnscrypt-proxy.toml
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] Anonymized DNS: routing [ams-dnscrypt-nl] via [anon-cs-dus4 anon-cs-dus3]
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] Firefox workaround initialized
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] [ams-doh-nl] OK (DoH) - rtt: 10ms
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] Anonymizing queries for [ams-dnscrypt-nl] via [anon-cs-dus3]
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] [ams-dnscrypt-nl] OK (DNSCrypt) - rtt: 18ms
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] Sorted latencies:
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] - 10ms ams-doh-nl
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] - 18ms ams-dnscrypt-nl
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] Server with the lowest initial latency: ams-doh-nl (rtt: 10ms)
Apr 09 18:22:45 raspberrypi dnscrypt-proxy[317798]: [2024-04-09 18:22:45] [NOTICE] dnscrypt-proxy is ready - live servers: 2
It's the expected output based on the DNS routing !
Configure dnsmasq
As we have dnscrypt service ready , It's time to configure dnsmasq to use the local running server to resolve the domains. This can be simply done by adding to /etc/dnsmasq.conf:
server=127.0.0.1#54
no-resolv
The no-resolv is needed to prevent dnsmasq from using /etc/resolv.conf
server
defined otherwise you will have DNS leak againFinally, restart dnsmasq using:
systemctl restart dnsmasq.service
Testing
The easiest way is to execute a DNS leak tests using https://www.dnsleaktest.com and https://browserleaks.com/dns
You should only see the configured DNS IP's. In my case, Netherlands IP's only:
As I am based on Belgium, It's working as expected based on the returned Locations 😎
In conclusion, setting up DNS-over-HTTPS on a Raspberry Pi is a proactive step towards bolstering your online privacy and security. By following these steps and leveraging the capabilities of your Raspberry Pi, you can ensure that your DNS queries remain private and secure from potential threats.
Whether you're a novice or an experienced user, implementing DoH on your Raspberry Pi is straightforward and rewarding. Stay tuned for more tips on optimizing your network security and privacy with Raspberry Pi.
Feel free to drop a comment if you need help or have questions!