Implementing DNS Encryption with DoH and DoT: A Guide to Secure Network DNS Setup

Introduction

Standard DNS lacks encryption capabilities, with all queries by default being transmitted as plaintext over UDP. Some responses with large byte results are truncated and transmitted via TCP (retrying in TCP mode), but these are still plaintext. In highly secure business environments, DNS encryption becomes crucial to avoid hijacking, pollution, and other security threats. This is where DNS over HTTPS (DoH) and DNS over TLS (DoT) come into play. Additionally, HTTPDNS can prevent operator hijacking issues by bypassing the ISP’s LDNS and directly requesting HTTPDNS services. This offers [domain hijacking prevention](https://cloud.tencent.com/product/httpdns?from_column=20065&from=20065), precise scheduling, and other features, but is mainly oriented towards mobile app scenarios. The resolution process is as follows:

DNS encryption

Returning to the main topic, dnscrypt-proxy serves as a DoH/DoT DNS forwarding service. Combined with existing DoH/DoT public services in the industry, it easily achieves DNS encrypted transmission.

Based on this, it is necessary to route domestic and foreign domains to different DoH/DoT resolutions to improve resolution efficiency and accuracy. This is achieved using dnsmasq + dnscrypt-proxy, with the architecture as follows:

DNS encryption

The installation and configuration of dnsmasq will not be detailed here; you can refer to the previous article.

1. Installing dnscrypt-proxy

1. Installing from Software Source

Most distribution software repositories come with dnscrypt-proxy included, and it’s recommended to choose this installation method as it will automatically set up the systemd service.

Distribution

Installation Command

Arch

pacman -Sy dnscrypt-proxy/yay -Sy dnscrypt-proxy

CentOS/RedHat

yum install -y dnscrypt-proxy

Debian/Ubuntu

apt-get install -y dnscrypt-proxy

2. Binary Installation

(1) Obtaining

Download the latest version from the releases page:

cd /opt  # Habitually place it in the /opt directory; you can also place it in the /etc directory or elsewhere
wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/2.1.2/dnscrypt-proxy-linux_x86_64-2.1.2.tar.gz

As of November 2022, the latest version is 2.1.2.

(2) Extracting and Creating a Symlink
tar xvf dnscrypt-proxy-linux_x86_64-2.1.2.tar.gz
mv linux-x86_64 dnscrypt-proxy
cd dnscrypt-proxy
ln -sf /opt/dnscrypt-proxy/dnscrypt-proxy /usr/sbin/dnscrypt-proxy
  • Rename for better readability, you’ll see an executable dnscrypt-proxy file and some sample configuration files after extraction.
  • Create a symlink to the executable in the PATH directory.
(3) Creating a systemd Service
vim /etc/systemd/system/dnscrypt-proxy.service

Insert the following content:

[Unit]
Description=Encrypted/authenticated DNS proxy
ConditionFileIsExecutable=/usr/sbin/dnscrypt-proxy

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/sbin/dnscrypt-proxy "-config" "dnscrypt-proxy.toml"

WorkingDirectory=/opt/dnscrypt-proxy

Restart=always

RestartSec=120
EnvironmentFile=-/etc/sysconfig/dnscrypt-proxy

[Install]
WantedBy=multi-user.target

Reload the systemd daemon:

systemctl daemon-reload

Enable at startup:

systemctl enable dnscrypt-proxy.service

2. Public Servers and Service Configuration Rewrite

1. Temporarily Test Resolution

The file example-dnscrypt-proxy.toml in the directory is the main configuration file. Copy it to use as the official configuration file:

cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml

Before starting via systemctl, it can be run in the foreground:

./dnscrypt-proxy "-config" "dnscrypt-proxy.toml"

Test if the resolution is normal:

./dnscrypt-proxy -resolve google.com

You should be able to resolve addresses normally. From the logs, you can see that by default, it requests built-in DoH servers. Since most of these DoH servers are overseas, the ability to access them securely is a fundamental requirement if you are in China.

2. DoH/DoT Service List

(1) Public Servers

The dnscrypt-proxy official provides a service list (may require secure internet access within China) where you can choose the servers to use as upstream DNS from the supported list.

Currently, Tsinghua and Alibaba’s DoH servers are the only supported ones within China:

(2) Public Servers Map

Additionally, a map search method is also provided, with the supported points marked on the map:

This facilitates selecting the closest DoH upstream server to you.

3. Configuration File Rewrite

Based on the need for domestic and overseas routing, two configuration files are required, one for domestic and one for overseas.

(1) Domestic Configuration

Most built-in functions of dnscrypt-proxy.toml are unnecessary. Here, dnscrypt-proxy.toml is rewritten as:

# Empty listen_addresses to use systemd socket activation
listen_addresses = ['0.0.0.0:5533']
server_names = ['alidns-doh','tuna-doh-ipv4']
cache_size = 4096
cache_min_ttl = 2400
cache_max_ttl = 86400
cache_neg_min_ttl = 60
cache_neg_max_ttl = 600

[query_log]

file = ‘/var/log/dnscrypt-proxy/query.log’

[nx_log]

file = ‘/var/log/dnscrypt-proxy/nx.log’

[sources]

[sources.’public-resolvers’] url = ‘https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md’ cache_file = ‘/var/cache/dnscrypt-proxy/public-resolvers.md’ minisign_key = ‘RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3’ refresh_delay = 72 prefix = ”

  • The listening address here is set to 5533 instead of 53 to prevent conflicts with system-built DNS services.
  • server_names are the defined upstream DoH/DoT servers and must be chosen from the service list.
(2) Foreign Configuration

Copy dnscrypt-proxy.toml to create a new file:

cp dnscrypt-proxy.toml dnscrypt-proxy-foreign.toml

Change server_names to overseas DoH/DoT servers and modify the listening port. The example after modification is as follows; other parts need not be edited:

listen_addresses = ['0.0.0.0:25533']
server_names = ['cloudflare','google']

4. Writing a systemd Service

The domestic configuration is already written in systemd, and the same applies to foreign configuration, requiring a process to listen and run.

vim /etc/systemd/system/dnscrypt-proxy-foreign.service

Insert the following content:

[Unit]
Description=Encrypted/authenticated DNS proxy
ConditionFileIsExecutable=/usr/sbin/dnscrypt-proxy

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/sbin/dnscrypt-proxy "-config" "dnscrypt-proxy-foreign.toml"

WorkingDirectory=/opt/dnscrypt-proxy

Restart=always

RestartSec=120
EnvironmentFile=-/etc/sysconfig/dnscrypt-proxy

[Install]
WantedBy=multi-user.target

Reload the systemd daemon:

systemctl daemon-reload

Enable at startup:

systemctl enable dnscrypt-proxy-foreign.service

3. Start and Test Validation

3.1. Start

Load a toml configuration file for both domestic and foreign servers, with the corresponding systemd services already set up. Next, start them and verify if they meet expectations.

systemctl start dnscrypt-proxy.service
systemctl start dnscrypt-proxy-foreign.service

The service runs properly, with 5533 listening for domestic domains and 25533 for foreign domains.

2. Test Validation

At this point, intelligent domestic and foreign routing has not been implemented, so first separately verify if both services resolve normally.

View the resolution log file:

tail -f /var/log/dnscrypt-proxy/query.log

Test different ports using the dig command to see which upstream DNS is used:

dig qcloud.com @192.168.1.72 -p 5533 +short
dig youtube.com @192.168.1.72 -p 25533 +short

This indicates that both services are operating well.

4. Implementing Intelligent Domain Splitting with Dnsmasq

1. Modifying Dnsmasq Upstream DNS

If dnsmasq isn’t yet installed and configured, refer to the previous article up to the step of achieving domestic and foreign routing on a dnsmasq dimension using dnsmasq-china-list.

Next, just change the upstream DNS to the machine running dnscrypt-proxy. If dnsmasq and dnscrypt-proxy are on the same machine, change it to the local address.

(1) Specifying Domestic Upstream DoH Listening Address

In China, modify accelerated-domains.china.conf in dnsmasq-china-list, replacing the IP with the domestic DoH listening address:

sed -i 's|114.114.114.114|127.0.0.1#5533|g' accelerated-domains.china.conf

The result should look like server=/0-100.com/127.0.0.1#5533

As I have two services running independently on two systems, replace it with the domestic DoH listening address of the machine running dnscrypt-proxy:

(2) Specifying Foreign Upstream DoH Listening Address

Since resolv.conf can’t specify a port, upstream DNS for foreign DoH needs to be defined in dnsmasq.conf. So here, /etc/resolv.conf writes the local address, and dnsmasq.conf adds a configuration to the foreign DoH listening address:

$ cat /etc/resolv.conf
nameserver 127.0.0.1
$ cat /etc/dnsmasq.conf
log-queries
log-facility=/var/log/dnsmasq.log
no-hosts
bogus-nxdomain=119.29.29.29
cache-size=1000
port=53
# The following configuration is added, #25533 is not a comment but specifies the port. If dnsmasq and dnscrypt-proxy run on the same machine, replace with 127.0.0.1#25533
server=192.168.1.72#25533
(3) iptables DNAT Rule

Based on the expansion scheme of #(2), this is an optional step. You can choose either method, applicable if dnsmasq and dnscrypt-proxy run on different machines.

If you prefer not to specify the server in dnsmasq.conf as a foreign DoH address and set it directly in /etc/resolv.conf, you only need to make a DNAT rule on the dnscrypt-proxy machine that directs port 53 to the foreign DoH listening port:

# Replace 192.168.1.72 with the internal IP of the dnscrypt-proxy machine
iptables -t nat -A PREROUTING -i ens192 -p tcp --dport 53 -j DNAT --to-destination 192.168.1.72:25533
iptables -t nat -A PREROUTING -i ens192 -p udp --dport 53 -j DNAT --to-destination 192.168.1.72:25533

At this point, modify the /etc/resolv.conf file content on the dnsmasq machine to:

nameserver 192.168.1.72

2. Verifying DoH/DoT Intelligent Splitting Scenarios

Use any internal machine and change the DNS resolution to the dnsmasq machine address, then access domestic and foreign domains for testing and validation.

Machine

Role/Service

192.168.1.71

Client, DNS pointing to dnsmasq

192.168.1.72

dnscrypt-proxy

192.168.1.73

dnsmasq

(1) Log Verification

If you follow my above configuration to set up, the corresponding log file paths are:

  • dnsmasq query log path: /var/log/dnsmasq.log
  • dnscrypt-proxy query log path: /var/log/dnscrypt-proxy/query.log

Use the dig command to test, and monitor the output of the corresponding log file:

It is clear that for domestic domain resolution, dnsmasq forwards to the domestic upstream DoH of dnscrypt-proxy; for foreign domain resolution, it forwards to the foreign upstream DoH. Cache records are controlled by dnsmasq, and repeated requests directly hit the cache, returning the result to the client.

(2) Packet Capture Verification

dig github.com is used as a verification example:

Deploy packet capture on both the dnsmasq and dnscrypt-proxy machines:

On the dnsmasq machine:

tcpdump -i any -nn -s 0 port 53 or port 5533 or port 25533 or port 443 -v -w dnsmasq.pcap

On the dnscrypt-proxy machine:

tcpdump -i any -nn -s 0 port 53 or port 5533 or port 25533 or port 443 -v -w dnscrypt-proxy.pcap

Later, download the .pcap files locally, and use Wireshark to analyze them as follows:

  • dnsmasq: Packet 9, receiving DNS query request from the client;
  • dnsmasq: Packet 10, dnsmasq requests the upstream dnscrypt-proxy;
  • dnscrypt-proxy: Packet 5, dnscrypt-proxy receives the DNS query request from dnsmasq;
  • dnscrypt-proxy: Packets 6-30, establish a TCP three-way handshake and TLS handshake with upstream Google DoH, encrypting DNS requests;
  • dnscrypt-proxy: Packets 31-33, receive encrypted response data;
  • dnscrypt-proxy: Packet 34, decrypts and returns the resolution result to dnsmasq;
  • dnsmasq: Packet 11, receives the resolution record;
  • dnsmasq: Packet 12, returns the result to the client.

It is clear to see, dnsmasq receives the request and forwards it to the foreign DoH server for processing. This process goes through TLS-encrypted transmission, greatly ensuring DNS security. The request is received and decrypted before returning to the client normally.

Summary

With this, dnsmasq + dnscrypt-proxy to implement domestic and overseas DoH/DoT split resolution is completed. The principle is very simple: the dnsmasq machine serves as the entry point, using the dnsmasq-china-list mainland domain whitelist for forwarding and splitting to upstream dnscrypt-proxy, and dnscrypt-proxy then forwards the encrypted results to corresponding DoH/DoT public servers.

Additionally, dnscrypt-proxy has load balancing capabilities, which can be specified in the toml configuration file by the lb_strategy parameter. Potential settings include:

  • first: Always choose the fastest server in the list
  • p2: Randomly choose from the top 2 fastest servers, default option
  • ph: Randomly choose among the fastest half of all servers
  • random: Randomly select from the Server list

Adjust based on different business scenarios, and select a few high-quality upstream DoH/DoT, moderately increasing the number of RS for optimal selection.

PDF version attached:

dnscrpt-proxy+dnsmasq advanced application – implementation of DoH, DoT.pdfdnscrpt-proxy+dnsmasq advanced application – implementation of DoH, DoT.pdf (Light version).pdf