Encrypting DNS queries on Linux

Security and privacy have for a while been something I’ve been more conscious of. 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 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.

Initial setup

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

One the preferred address is identified, find which server it is associated with in the resolvers file, and note down the first column here (e.g. 4armed or 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. Firstly, the resolver server needs to be set. This can be configured using the following command:

systemctl edit --full dnscrypt-proxy.service

By default, the server is set to dnscrypt.eu-nl, so change this 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

To allow the service to be used with a local DNS cache, 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 --full dnscrypt-proxy.socket

Here, both the ListenStream and ListenDatagram need to be set to 127.0.0.1:, where is 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. A DNS caching service will then need to be configured accordingly.

For simplicity, I use a local DNS caching application called Rescached. I chose this service because it does not have many of the features I do not need currently, such as hosing one’s own DNS server. All it does is cache DNS queries into memory, up to a certain limit, and clear some of the entries when it’s full. When the service is stopped, the cache is stored onto disk, for later usage. One minor disadvantage is that, while DNS entries do expire, there are no settings to configure this.

Once installed, Rescached can be configured by editing the /etc/rescached/rescached.cfg file. Here, the server.parent entry must be set to 127.0.0.1: to direct queries to the encryption service.

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:

chattr +i /etc/resolv.conf

After this, enable the services:

systemctl enable dnscrypt-proxy.socket

systemctl enable rescached.service

Note you should not enable the DNS service directly, as the socket will do this. The services may be started here, however a reboot is likely needed for the new settings to take effect. To confirm that the service is being used, run the following command:

dig google.com

Any server can be used instead of google.com. In the output, there should be information like this:

;; 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

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