How to properly configure dnsmasq for DNSCrypt v2 to prevent leaks to ISP

As DNSCrypt v2 (https://github.com/jedisct1/dnscrypt-proxy) matured, I started using it as my only approach for encrypting DNS traffic. Many thanks to contributors of DNSCrypt v2!!! While the posted wiki guide on how to install and configure DNSCrypt v2 (https://github.com/jedisct1/dnscrypt-proxy/wiki/Installation-on-OpenWRT) is a good start, it misses some important configuration points that prevent leaks to ISP. Specifically - it leaves dnsmasq configured to use resolv.conf file, which can leak DNS traffic to the ISP. This can be easily reproduced by running tests (e.g. https://www.dnsleaktest.com/ and seeing your ISP listed in there) or just shutting down DNSCrypt and still be able to run DNS queries just fine.

So, I'm trying to create a proper list of DNSCrypt v2 configuration instructions for OpenWRT 18.06+ and need your help in figuring out on how to make it right. Here is my current configuration for domain "foo.com" (unless you use "lan" for the domain), which I called it out here, so you know where it needs to go:

  • /etc/config/dhcp (configure to use dedicated DNS server for non-LAN related queries):
config dnsmasq
    option domainneeded '1'
    option boguspriv '1'
    option filterwin2k '1'
    option localise_queries '1'
    option rebind_protection '1'
    option rebind_localhost '1'
    option local '/foo.com/'
    option domain 'foo.com'
    option expandhosts '1'
    option nonegcache '0'
    option authoritative '1'
    option readethers '1'
    option leasefile '/tmp/dhcp.leases'
    option localservice '1'

    # Ignore ISP's DNS:
    option noresolv '1'
    # Change /etc/resolv.conf to direct local router processes to use local dnsmasq:
    option localuse '1'
    list server '127.0.0.53'
  • /etc/dnsmasq.conf (to recognize local requests, so they are not sent to the DNS server):
# No forward list:
server=/lan/
server=/internal/
server=/intranet/
server=/private/
server=/workgroup/
server=/10.in-addr.arpa/
server=/16.172.in-addr.arpa/
server=/168.192.in-addr.arpa/
server=/254.169.in-addr.arpa/
server=/d.f.ip6.arpa/
  • /etc/config/dnscrypt-proxy.toml (only the parts that were changed from the default configuration):
listen_addresses = ['127.0.0.53:53']
blacklist_file = '/etc/config/blacklist.txt'
  • /etc/config/blacklist.txt (just in case some process connects to DNSCrypt v2 directly):
*.foo.com

*.test
*.onion
*.localhost
*.local
*.invalid
*.bind
*.lan
*.internal
*.intranet
*.private
*.workgroup

*.10.in-addr.arpa
*.16.172.in-addr.arpa
*.168.192.in-addr.arpa
*.254.169.in-addr.arpa
*.d.f.ip6.arpa

*/etc/config/firewall (prevent clients on the local network from using external DNS servers):

config redirect
    option name 'Divert-DNS'
    option src 'lan'
    option proto 'tcpudp'
    option src_dport '53'
    option dest_port '53'
    option target 'DNAT'

The above configuration tells dnsmasq to not use the resolv.conf file and just forward all non-local DNS requests to the dedicated DNSCrypt v2 server. The DNSCrypt v2 will also reject requests for local addresses. Additionally, all requests to external DNS servers from the local clients will be redirected to the router.

Comments and suggestions are welcome!

Update history:
04/04/19: Added firewall rule to prevent clients on the local network from using external DNS servers
03/25/19 : Updated above instructions with suggestions from @vgaetera

4 Likes
cp /usr/share/dnsmasq/rfc6761.conf /tmp/rfc6761.conf
cat << EOF >> /tmp/rfc6761.conf
server=/10.in-addr.arpa/
server=/16.172.in-addr.arpa/
server=/168.192.in-addr.arpa/
server=/17.172.in-addr.arpa/
server=/18.172.in-addr.arpa/
server=/19.172.in-addr.arpa/
server=/20.172.in-addr.arpa/
server=/21.172.in-addr.arpa/
server=/22.172.in-addr.arpa/
server=/23.172.in-addr.arpa/
server=/24.172.in-addr.arpa/
server=/25.172.in-addr.arpa/
server=/26.172.in-addr.arpa/
server=/27.172.in-addr.arpa/
server=/28.172.in-addr.arpa/
server=/29.172.in-addr.arpa/
server=/30.172.in-addr.arpa/
server=/31.172.in-addr.arpa/
server=/corp/
server=/d.f.ip6.arpa/
server=/home/
server=/internal/
server=/intranet/
server=/lan/
server=/local/
server=/private/
server=/test/
EOF
sort -u /tmp/rfc6761.conf > /usr/share/dnsmasq/rfc6761.conf
service dnsmasq restart
1 Like

Thanks Vlad! I tried to figure out where dnsmasq gets its default list of local domains, but could not find it.

Do you know if this is going to work fine with a ZeroConf service? I've never used ZeroConf, but saw that it requires /etc/dnsmasq.conf changes to advertise services on a local network. (e.g. see https://openwrt.org/docs/guide-user/services/print_server/p910nd.server)

What you suggested is a good way to go, but requires changing /usr/share/dnsmasq/rfc6761.conf file during every upgrade. So, let me ask you what you think about the following alternative? It is possible to add the above list of local domains to dnsmasq through the following configuration option in /etc/config/dhcp file:

config dnsmasq
        option serversfile '/etc/config/local_domains.txt'

But this configuration option will conflict with services like AdBlock (which uses that config option to set its list of blocked domains). However, since we're talking about DNSCrypt v2 here, it is now capable of doing ad-blocking too and has various feeds too. So, it is still doable.

What do you think?

/etc/dnsmasq.conf is loaded by the init-script, but it is effectively empty.

Also there's an option:

So, you can simplify the config:

mv -n /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
cat << EOF > /etc/dnsmasq.conf
# No forward list
rev-server=10.0.0.0/8
rev-server=172.16.0.0/12
rev-server=192.168.0.0/16
...
server=/home/
server=/private/
...
EOF
1 Like

That's the best way to go! Thanks!

@vgaetera, I just updated the original post. Do you mind taking a look at it when you get a chance to check for any errors?

Thanks

It looks like you are using Dnsmasq as a local system resolver.
You should add an exception for NTP-provider to not get locked if system time is not synchronized.

Good point, but I believe this problem is not applicable to DNSCrypt v2, since it is configured with fallback_resolver = '9.9.9.9:53'. I'll need to double check this.
DNSCrypt v1 on the other hand requires precise time, so it needs one of its ntp servers listed in dnsmasq with fixed IP just as you suggested.

1 Like

I've incorporated the proposed configuration changes into DNSCrypt v2 wiki: https://github.com/jedisct1/dnscrypt-proxy/wiki/Installation-on-OpenWRT

2 Likes

Is there any way to use 2 separate blacklist.txt files in dnscrypt-proxy.toml config file (first for adblock and second for local domains)?

At the moment, there is no way to provide more than one entry for the blacklist_file configuration option. But anyone who builds their own black lists from external sources (e.g. adblock, etc.) should use a file with local domains that will be included into the black list by its generator. All examples provided with DNSCrypt v2 include the following line to do that:

# Local additions
file:domains-blacklist-local-additions.txt

I guess this can also be filed as an enhancement to configuration options of DNSCrypt v2 to support more than one blacklist file.

1 Like

Because of this, I am forced to use one file for domain blocks, generated by Adblock. The Adblock itself has the ability to manually add additional domains, which will then be added to the shared file adb_list.overall. It is in this manual list that you cannot insert records of type 10.in-addr.arpa

I insist that the old way of configuring Dnscrypt-Proxy is saved in the wiki.

Please clarify which component did you plug Adblock into?

If you are using Adblock with dnsmasq (the one that gets installed through opkg), then there is no need to add additional local domains into /tmp/adb_list.overall file - just add them into /etc/dnsmasq.conf as stated above. This will keep Adblock as a separate optional component without affecting the proper setup for dnsmasq.

If the Adblock you're referring to is a blacklist generator for DNSCrypt v2, it does not use /tmp/adb_list.overall file, but has the ability to add local custom list into the final file, which will be consumed by DNSCrypt through blacklist_file option.

I insist that the old way of configuring Dnscrypt-Proxy is saved in the wiki.

The old config had ISP leaks and deadloops in the configuration. Why don't you change /etc/dnsmasq.conf to make it right?

First, because it is not necessary: dnsmasq knows for what local domains it responds (look at the logs at boot). Second, there was no DNS-leak. Thirdly, the common file adb_list.overall is just loaded through the dnscryp-proxy.toml from the /tmp folder:

blacklist_file = '/tmp/adb_list.overall'

Let's break it down:

First, because it is not necessary: dnsmasq knows for what local domains it responds (look at the logs at boot).

You're correct - it knows about 7 local domains: one configured in domain option in /etc/config/dhcp and 6 others coming from https://github.com/openwrt/openwrt/blob/master/package/network/services/dnsmasq/files/rfc6761.conf. Its default configuration is not aware that queries for some "*.arpa" names belong to local network only and should not be forwarded to external DNS server. So, making changes to /etc/dnsmasq.conf is important.

Second, there was no DNS-leak.

The reason why you did not experience leaks was because your configuration has the following options set, which was not mentioned in the old wiki:

    # Ignore ISP's DNS:
    option noresolv '1'
    # Change /etc/resolv.conf to direct local router processes to use local dnsmasq:
    option localuse '1'

Thirdly, the common file adb_list.overall is just loaded through the dnscryp-proxy.toml from the /tmp folder

You seem to be trying to mix the configuration for OpenWRT's Adblock (which generates and uses file /tmp/adb_list.overall) with the blacklist generator for DNSCrypt v2 (which suggests to use /etc/config/blacklist.txt file and have it generated through the provided python script : https://github.com/jedisct1/dnscrypt-proxy/blob/master/utils/generate-domains-blacklists/domains-blacklist-all.conf).

While there is nothing wrong with using /tmp/adb_list.overall file as the blacklist for DNSCrypt, it confuses people here who are trying to help you.

The point of this thread it to get a proper and safe configuration for DNSCrypt. If you have any suggestions on how to make it better and proper, please share them.

Hey guys!
Im still having loop. >.<

I did everything, except some peculiarities from my lan:
10.0.0.1/255.255.255.224
192.168.1.2:1 Modem<->Router (to dsl modem get ntp)
QoS

I follow: https://github.com/jedisct1/dnscrypt-proxy/wiki/Installation-on-OpenWRT
Updated from this post.

Look:

https://pastebin.com/ksEWNfKd

Some hint?
Many thanks!

Yes, it does look like a loop between dnsmasq and dnscrypt. You must re-check your configuration and make sure that:

  • dnsmasq does not send requests about local network to dnscrypt - this is done through /etc/dnsmasq.conf changes described on the wiki
  • dnscrypt ignores requests about local network - this is done through blacklist.txt (instead of the forwarding-rules.txt that was listed in the old wiki)
  • make sure that blacklist.txt is placed in the same folder as dnscrypt-proxy.toml, so it gets picked up
  • you're using OpenWRT 18.06.2+ - this is needed for localuse option of dnsmasq

The 10.* + 192.168.* network layout should not matter. And QoS is also unrelated.

Hey!
dnsmasq.conf

dhcp-option=252,"\n"
server=/lan/
server=/internal/
server=/intranet/
server=/private/
server=/workgroup/
server=/10.in-addr.arpa/
server=/16.172.in-addr.arpa/
server=/168.192.in-addr.arpa/
server=/254.169.in-addr.arpa/
server=/d.f.ip6.arpa/

blacklist.txt (Same folder dnscrypt-proxy.toml, dnscrypt-proxy) Running on usb stick.
I use -config dnscrypt-proxy.toml. Using full path always.
I dont use cloaking, forward or whitelist.

Command:

dnscrypt-proxy -config dnscrypt-proxy.toml

Config file pass on -check.

I remove all my firewall custom rules.

blacklist.txt

*.test
*.onion
*.localhost
*.local
*.invalid
*.bind
*.lan
*.internal
*.intranet
*.private
*.workgroup

*.10.in-addr.arpa
*.16.172.in-addr.arpa
*.168.192.in-addr.arpa
*.254.169.in-addr.arpa
*.d.f.ip6.arpa

dhcp:

       option localservice '1'
       list addnhosts '/plugin_root/adblock/block.hosts'

       option noresolv '1'
       option localuse '1'
       list server '127.0.0.53'

resolv.conf

search lan
nameserver 127.0.0.1

Many Thanks!

Hello,
I tried this howto a couple of times, it does not work in my hands.
As stated by @wind here, a problem is with the 'option noresolv' setting:
with

option noresolv 1
cat /etc/resolv.conf
'# Interface wan
nameserver 217.0.43.113
nameserver 217.0.43.97

ISP DNS servers. Changing the option I get:

option noresolv 0
cat /etc/resolv.conf
search lan
nameserver 127.0.0.1

However, also with 'option noresolv 0' the DNS servers as listed by www.dnsleaktest.com are all from the ISP. By contrast this: DNSSEC Resolver Test (http://dnssec.vs.uni-due.de/): "Yes, your DNS resolver validates DNSSEC signatures"

I have configured all several times, including prior complete reset to factory setting in order to avoid any interference with customised changes in configuration files.

Here is the dnsmasq section of my /etc/config/dhcp

cat /etc/config/dhcp
config dnsmasq
option domainneeded '1'
option boguspriv '1'
option filterwin2k '0'
option localise_queries '1'
option rebind_protection '1'
option rebind_localhost '1'
option local '/lan/'
option domain 'lan'
option expandhosts '1'
option nonegcache '0'
option authoritative '1'
option readethers '1'
option leasefile '/tmp/dhcp.leases'
#option resolvfile '/tmp/resolv.conf.auto'
option nonwildcard '1'
option localservice '1'
option serversfile '/tmp/adb_list.overall'
# Ignore ISP's DNS:
option noresolv '1'
# Change /etc/resolv.conf to direct local router processes to use local dnsmasq:
option localuse '1'
list server '127.0.0.53'

The firewall was modified by copy/paste exactly as above. The CA-bundle is installed, same for blacklist.txt.

Also bizarre is:

dnscrypt-proxy -list
[2019-06-27 11:22:01] [FATAL] Unable to load the configuration file [dnscrypt-proxy.toml] -- Maybe use the -config command-line switch?
ls -l /etc/config/
....
-rw-r--r-- 1 root root 16242 Jun 27 11:38 dnscrypt-proxy.toml

it seems that the conf file is not used?! (verified it is toml and not tom1)

cat /etc/init.d/dnscrypt-proxy |grep CONFIG
CONFIGFILE=/etc/config/dnscrypt-proxy.toml
procd_set_param command "$PROG" -config "$CONFIGFILE"
procd_set_param file "$CONFIGFILE

I don't understand this. Hope someone can advise?

Cheers
Oscar

@oscar, please check the following:

  1. You're using OpenWRT 18.06.2 or newer. That's needed for the option localuse to work properly. The following command returns "8" on 18.06.2 build: grep -c localuse /etc/init.d/dnsmasq
    I have not tried 18.06.3 yet, so let me know if you're using that.
  2. Verify that DNSCrypt is configured properly by pointing it to the configuration file:
    dnscrypt-proxy -config /etc/config/dnscrypt-proxy.toml -check
    and dnscrypt-proxy -config /etc/config/dnscrypt-proxy.toml -list
  3. Check that DNSCrypt is listening on 127.0.0.53:53 TCP/UDP:
    > logread | grep dnscrypt | grep listening
    Mon Jun 24 08:03:15 2019 daemon.notice dnscrypt-proxy[8288]: Now listening to 127.0.0.53:53 [UDP]
    Mon Jun 24 08:03:15 2019 daemon.notice dnscrypt-proxy[8288]: Now listening to 127.0.0.53:53 [TCP]
    
  4. Check that dnsmasq is using the proper server "127.0.0.53#53":
    > logread | grep 'dnsmasq.*using nameserver'
    Mon Jun 24 08:02:45 2019 daemon.info dnsmasq[8256]: using nameserver 127.0.0.53#53
    
  5. Confirm that the auto-file points to the ISP-box, but the resolv.conf points to 127.0.0.1:
    > cat /tmp/resolv.conf.auto
    # Interface wan
    nameserver 192.168.1.1
    search router.home
    > cat /tmp/resolv.conf
    search lan
    nameserver 127.0.0.1
    
  6. Since resolv.conf file is only used by the processes on your OpenWRT machine (a.k.a. the router), all other clients are going to use whatever DHCP server instructs them to use. If you have a Windows client that uses the router as the DHCP server, check that it gets configured with "DNS Server" pointing to your router's IP - ipconfig /all
    And verify that the router provides name resolutions for the Windows client: nslookup abc.com (it should print router's IP in the "Server Address")
    This is important, because clicking on URL http://www.dnsleaktest.com/ happens on the Windows machine and router's firewall should redirect external DNS requests to DNSCrypt.