Encrypting DNS queries on Linux

Security and privacy have for a while been something of which I’ve been more conscious. While I don’t particularly expect to get hacked or have information leaked, using encryption when possible seems to be overall beneficial, particularly when it has little to no impact on performance. Although I’ve opted to use browser extensions to enable HTTPS when available, I recently became curious about DNS. As I expected, these queries, which fetch IP addresses from text domain names, are not normally encrypted. This means should any packet sniffing be done on data being sent, it is possible to determine what websites a person is visiting. What I did to circumvent this is use DNSCrypt, which is a free DNS encryption service.

The DNS protocol, in general circumstances, will have the client send a query with a domain name (e.g. google.com), which is sent to the main DNS server (by default it’s through one’s ISP). Once a response is received with the IP address, the client can then connect to the desired server. To use the DNSCrypt protocol, only a certain set of servers can be contacted. As one would want to reduce query time to these servers in order to keep web-browsing feeling quick, it’s important to choose a server that’s close to the client machine. Thankfully, in order to eliminate the guess work out of this, there are tools that can be used to determine the optimal server.

Choosing the best DNS server

Firstly, download the dnscrypt-resolvers.cvs list from this github page, which contains an up-to-date list of DNS resolvers. Google has a tool called namebench which can be used to measure the query response time of DNS servers provided, amongst other things. Most Linux distributions should have a package available for this tool. On Arch it can be found on the AUR. Be sure tk is also installed to enable the graphical front-end. Next, the IP addresses with ports must be parsed out of the resolvers file. The following command can be used to obtain the list, separated by commas:

grep -o '\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}\(\:[0-9]*\)\?' dnscrypt-resolvers.csv | awk '{printf "%s,",$i}'

The output can either be redirected into a file, or directly copied from the terminal. Paste the list into the DNS servers box in the namebench tool. Before running the test, be sure to un-check the boxes to test global and local servers, as these are not desired. After this, run the benchmarks. This will likely take several minutes, as (by default) each server will be queried 250 times. It’s noteworthy that it can test using browsing history, though this is not necessary. Once the tests are completed, an HTML page is generated and opened containing a detailed list of the results. One thing to note is that the results will list potential DNS resolution failures or hijackings, which may not be completely accurate. These compare the results obtained with expected IP addresses, but does not take into consideration IP localization. For example, services like Paypal will connect the user to a local server, which means a dynamic DNS scheme is being used, resulting in hijacking notices.

Configuring DNSCrypt

Once the preferred address is identified, find the server to which it is associated in the resolvers file, and note down the first column here (e.g. 4armed, cisco). This will be used to configure DNSCrypt. Again most Linux distributions should have packages for this. In Arch it’s the dnscrypt-proxy package. Once installed, a few configurations need to be changed. Most of these can be found in the /etc/dnscrypt-proxy.conf file. Firstly, the resolver server needs to be set. This can be configured by editing the following setting:

ResolverName <DNS server>

By default, the server is set to random, so this should be changed to the desired server. Although this alone would generally be sufficient to use the service (after editing the resolv.conf file) it’s highly recommended to use this alongside a local DNS cache. By default, Linux does not cache DNS results, meaning every time a domain name has to be resolved to an IP, it queries for it. As it is very likely the server used here has a longer response time than the default, browsing may feel slower. By using a DNS cache, results from queries can be stored, and retrieved more quickly.

Working with DNS Cache

The current version of DNSCrypt now includes a built-in DNS cache. It is simply a matter of assuring it’s enabled in its settings:

LocalCache on

To allow the service to be used with a separate DNS caching application, some port routing has to be configured. Typically, the DNS server would be set to localhost or 127.0.0.1, however this needs to be routed to the caching service, which then routes to the DNS encryption service. To do this, set the ports by editing the socket:

systemctl edit dnscrypt-proxy.socket

Add the following to the newly created file:

[Socket]
ListenStream=
ListenDatagram=
ListenStream=127.0.0.1:<port>
ListenDatagram=127.0.0.1:<port>

Here, both the ListenStream and ListenDatagram point to the localhost, with a user-specified port. I recommend using a port that has 4 digits, in order to avoid conflicts with other ports. The default DNS service port is 53, so be sure not to use that. The DNS caching service will then need to be configured accordingly. Note that the duplicate entries are to clear any existing versions before applying the overwritten ones.

 

Enabling the services

After this, one last change must be made before services are enabled. Edit the nameserver entry in /etc/resolv.conf to 127.0.0.1. This will direct all DNS queries to the caching service. Some other services may edit the resolve.conf file, and while there are many ways of preventing this, I find the simplest to set the file to immutable (after editing it):

chattr +i /etc/resolv.conf

After this, enable and start the services:

systemctl enable dnscrypt-proxy.service <optional cache>

systemctl start dnscrypt-proxy.service <optional cache>

The services should be active immediately (unless the cache requires further configuration or a reboot). To confirm that the service is being used, run the following command:

dig google.com #A different domain may replace google

In the output, there should be information resembling:

;; Query time: 36 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)

If the server is 127.0.0.1, then it all worked correctly. Running the command a second time should retrieve the IP address from cache, and result with a query time of 0.

Advertisements

2 thoughts on “Encrypting DNS queries on Linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s