Encrypting and filtering DNS queries using dnsmasq + dnscrypt-proxy in a local server

2021-06-09

(last time edited: 2021-06-20)

tags: dns, encryption, networks

I'm writing this blog to explain in a few simple steps how to set up a local DNS cache that is in both way, secure and private. A local DNS that will run in your Local Area Network, filter, query (with encryption) any public, open and sane DNS resolver from the Internet.

This local DNS server can help us to filter any site we want, completely block malicious IPs, obliterate ads and tracking, evade censorship, avoid man-in-the-middle attacks and give corporations the middle finger because it makes us feel powerful, even if they lose $0,0000001 cents for every logged query. And most important encrypt every query from point to point so third parties cannot read it.

Anyways, I was successfully able to setup and experiment with configurations in both Void Linux and Alpine Linux. Both distributions have packaged the software correctly as I expected.

What are DNS resolvers? Basically they are servers that help you link domains to IPs. Without a Domain Name System we would not have a working Internet as we know it.

Since the creation of DNS not so many people seem to care about the importance of this backbone. It's ignored by most users yet corporations know how to monetize the big data DNS provides. Other hackers, from low to big hierarchies, know how to maliciously infect them for their own benefits.

Anyways, who cares about other people, privacy fundamentalism and the mental masturbatory activism? These things goes nowhere. Money rules the world.

Let's just learn to administrate networks because it's fun and we have nothing better to do.

Requirements for this guide:

The /etc/hosts file

There are multiple lists of hosts actively maintained by different people. The most well known and easy to find are Block List Project, MVP and Steven Black hosts file.

I recommend you to experiment with them and even add a personal list of hosts you wanna block.

The following is a example of a /etc/hosts file blocking Digital Ocean:

127.0.0.1 localhost.localdomain localhost
::1 localhost.localdomain localhost

0.0.0.0 digitalocean.com

For further information you can look up the manpage.

$ man hosts

Understanding this little bit of information we can proceed to block and filter all the domains we want. But we may need other tools such as dnsmasq or dnscrypt-proxy.

dnsmasq automatically reads the /etc/hosts file by default.

dnsmasq

dnsmasq (short for DNS masquerade) is a lightweight, easy to configure DNS forwarder, designed to provide DNS (and optionally DHCP and TFTP) services to a small-scale network. It can serve the names of local machines which are not in the global DNS.

dnsmasq is a lightweight daemon program written in C. It forwards DNS queries and is also able to cache, filter and distribute resolved queries in a network.

dnsmasq can be used standalone without any other DNS resolver talking to it and does not provide encryption by default. For encryption we must relay on dnscrypt-proxy but that will be explained later in the guide. We won't be using dnsmasq's DHCP/TFTP/PXE capabilities because we already use our router for that.

First we install dnsmasq.

In Void Linux:

In Alpine Linux:

dnsmasq configuration is fairly simple.

There is no big need to configure dnsmasq completely. We can disable DHCP and make dnsmasq to not read the /etc/resolv.conf but this is not completely necessary.

We only need to tell dnsmasq to listen on upstream localhost on port 3000.

Edit /etc/dnsmasq.conf

server=127.0.0.1#3000

Don't be so anxious. This is the first step. dnsmasq will only receive and cache all the queries retrieved by dnscrypt-proxy.

If you want you can already start up the service.

In Void Linux:

In Alpine Linux:

dnsmasq logging

For further debugging you can enable logging.

Edit /etc/dnsmasq.conf

log-queries

In Void Linux using socklog-unix service enabled:

In Alpine Linux:

NOTICE: I only recommend logging for debugging.

dnscrypt-proxy

DNSCrypt is a protocol that encrypts, authenticates and optionally anonymizes communications between a DNS client and a DNS resolver. It prevents DNS spoofing. It uses cryptographic signatures to verify that responses originate from the chosen DNS resolver and haven’t been tampered with.

It is an open specification, with free and open source reference implementations, and it is not affiliated with any company nor organization.

Free, DNSCrypt-enabled resolvers are available all over the world.

dnscrypt-proxy is a program written in Go that uses the DNSCrypt protocol to talk to public DNS servers on the Internet. Its most important feature is encryption and easily shares DNS resolved domains with other local DNS daemons, for example dnsmasq.

With dnscrypt-proxy we can also use another DNS encryption protocol called DNS over HTTPS but we won't be using it. In fact, we will use servers with DNSCrypt protocol only.

Install dnscrypt-proxy.

In Void Linux:

In Alpine Linux:

Edit the configuration file.

In Void Linux:

In Alpine Linux:

Configuration example:

server_names = ['plan9-ns1', 'plan9-ns2']
listen_addresses = ['127.0.0.1:3000']
doh_servers = false
require_dnssec = true
block_ipv6 = true
# fallback_resolvers = ['9.9.9.9:53', '8.8.8.8:53'] # not needed
netprobe_address = '172.98.193.42:53' # currently using Backplane DNS

We are first declaring some server names. You can find a list here. Choose any you may like, and please don't use a DoH server.

At the time of picking public DNSCrypt servers you can take in consideration many things, such as geographical location, ads or malware filtering, logging, laws, censorship, maintainers, what they want to accomplish offering the service, etc.

Also adding more DNS servers than 2 is not very encouraged in my personal opinion.

Secondly, the listen_addresses directive is pointing to our dnsmasq local DNS cache server to pass all the information we retrieved from the Internet. dnsmasq will be waiting happily for every resolved domain. If you look closely the port used is an ephemeral 3000 number.

There are many other options that can be useful. Such as disabling doh_servers and block_ipv6 or adding a netprobe_address to check for internet connectivity, or even managing fallback_resolvers in case our dnscrypt-proxy cannot reach any DNSCrypt server. And finally enabling DNSSEC.

And you could always use a different netprobe_address. For example using the public 1.1.1.1 DNS or something else.

Recommended servers you can pick:

* Servers listed above maintain a non-logging, non-filtering and DNSSEC enabled configuration. These DNS are also listed between many other servers in the public list.

dnscrypt-proxy logging

For further debugging you can enable logging.

In Void Linux:

use_syslog = true

In Alpine Linux:

file = '/var/log/dnscrypt-proxy/query.log'
use_syslog = true

Read the logs.

# tail -f /var/log/messages

NOTICE: I only recommend logging for debugging.

Configuring the DHCP server

Here comes the hard part if you never messed around with networks.

It's time to configure the DHCP server running in our router.

The computer/laptop/raspberry server running dnsmasq + dnscrypt-proxy must be have assigned at all times a static IP in the Local Area Network (LAN).

Address reservation may come VERY handy. You don't wanna lose connection to your local DNS everytime the network interface releases and asks for a new local IP right? That is absolutelly annoying.

This is an example of my local address reservation and my multiple devices. Every device has its own IP.

blogimg

Restarting dhcpcd / ndhc client daemons will always assign the declared IP. But you don't even need to relay on dhcp clients. You can do it manually using ip commands.

In my case, I also configure the DHCP server pointing to the local machine running dnsmasq + dnscrypt-proxy. I do not care about having a secondary DNS fallback. Fuck that.

blogimg

Finally, with dhcpcd or ndhc client daemons, all your LAN devices will populate /etc/resolv.conf with the correct DNS information retrieved from your router's DHCP server.

In this case below, my computer running dhcpcd assigned 192.168.1.111 as DNS server. That address is the Primary DNS IP I assigned in the router.

# Generated by dhcpcd from eth0.dhcp, eth0.dhcp6
# /etc/resolv.conf.head can replace this line
nameserver 192.168.1.111
nameserver 0.0.0.0
nameserver ::
# /etc/resolv.conf.tail can replace this line

If you are configuring your PC's network interface card manually (not dhcpcd) using with iproute2 (ip) commands, you must add the DNS IP manually in /etc/resolv.conf

And finally, the nameservers in your server running dnsmasq + dnscrypt-proxy should be pointing at localhost. Like this:

nameserver 127.0.0.1

Why? Because otherwise we will not be reading any DNS queries, therefore no proper Internet connection.

DNS Leaks

A DNS leak refers to a security flaw that allows DNS requests to be revealed to ISP DNS servers, despite the use of a VPN service to attempt to conceal them. Although primarily of concern to VPN users, it is also possible to prevent it for proxy and direct internet users.

In this guide I'm not especifically using a VPS since they are snake oil. They are just bandwidth resellers, data hoarders and malicious parties. But I don't wanna turn this into an opinion chamber.

Let's talk about DNS leaks. DNS leaks are as common as you may think. They will ever happen if you use DoH (DNS over HTTPS) or DNSCrypt protocol. There is always a third party (VPN, ISP, website) that will know who are you connecting to.

You can easily test your DNS leaks in sites like this: DNS leak test

There are multiple sites offering similar services for testing network privacy.

We at least want to achieve a few things here:

How can we know if the information is fully encrypted? Easy! Reading the outgoing and incoming UDP packets from our local server.

Simply install TCPDUMP in your local server.

This tool is offered in all Linux distributions and widely used.

Once you've it installed run TCPDUMP as root to read packets.

Replace the primary and secondary (or as many DNSCrypt servers you are connecting to).

# tcpdump -X -i eth0 host PRIMARY_DNSCRYPT_SERVER_IP or host SECONDARY_DNSCRYPT_SERVER_IP

Final note

It is very possible one could use other programs such as unbound and replace dnsmasq but I haven't tried yet.

DNS queries always talk via UDP port 53. TCP is not used unless specified.

Happy DNSing!