There is already lots of literature regarding DNS sinkholes and it is a common term in Information Security. In my case, I wanted to give it a try on FreeBSD 10 but I didn’t want to make use of Bind since it was removed from the base distribution in favor of Unbound.

The setup will have the following steps:

  • Create a jail where the service will be configured (not explained because there is lots of examples in Internet)
  • Install Unbound
  • Basic Unbound configuration
  • Configure Unbound to block DNS queries
  • Choosing block lists available in Internet
  • Updating the block lists
  • Bonus: use dnscrypt to avoid DNS spoofing
  • Final Unbound configuration file

Configuring our DNS sinkhole

Installing Unbound

I ran my test in FreeBSD 10.1. Sadly, it ships  Unbound v. 1.4.x, that is quite old and lacks some nice features. In the end, I had to install dns/unbound form the ports, that currently installs v.1.5.9.

If you are using a most recent FreeBSD distribution (e.g. FreeBSD 10.3), you will not need to install the port.

The only difference is that you will need to use local_unbound_enable=”YES” in /etc/rc.conf instead of unbound_enable=”YES” and the configuration file will be located in /etc/unbound/unbound.conf instead of /usr/local/etc/unbound/unbound.conf.

Basic Unbound configuration

First, we have to download the root-hints, to allow our dns cache to find the right master DNS servers.

# fetch ftp://ftp.internic.net/domain/named.cache -o /usr/local/etc/unbound/root.hints

Then, we edit the unbound.conf.

server:
        interface: 10.10.10.10

        #who can use our DNS cache
        access-control: 10.10.10.0/24 allow

        logfile: "/usr/local/etc/unbound/logs/unbound.log"
        username: unbound
        directory: /usr/local/etc/unbound
        chroot: /usr/local/etc/unbound
        pidfile: /usr/local/etc/unbound/unbound.pid
        verbosity: 1
        root-hints: /usr/local/etc/unbound/root.hints

#remote-control allows us to use the unbound-control
#utility to manage the service from the command line
remote-control:
        control-enable: yes
        control-interface: /usr/local/etc/unbound/local_unbound.ctl
        control-use-cert: no


Please, notice, that all files are located in /usr/local/etc/unbound/. If you are not using the version provided by de ports tree, the base directory will be/var/unbound/ instead.

The last step is to enable and to start the service

# sysrc unbound_enable="YES"

# service unbound start

With this setup, we have a basic DNS cache configurated in our network. Now, you should be able to query the DNS server listening on  10.10.10.10

# host www.google.com 10.10.10.10

Using domain server:

Name: 10.10.10.10

Address: 10.10.10.10#53

Aliases: 


www.google.com has address 74.125.68.147

www.google.com has address 74.125.68.105

www.google.com has address 74.125.68.103

www.google.com has address 74.125.68.99

www.google.com has address 74.125.68.104

www.google.com has address 74.125.68.106

www.google.com has IPv6 address 2404:6800:4003:c02::6

Configure Unbound to block DNS queries

The classic trick in DNS sinkholes is to define authoritative zones in the DNS cache, that will return a defined static IP address (e.g.127.0.0.2) to identify in the logs (or in network devices) when somebody is trying to connect to a blocked domain.

In Unbound, it is a bit more difficult because it is only a basic DNS cache service and lacks some features, but there are some ways around.

unbound.conf(5) has the local-zone directive and it is used to define local DNS zones but we will “abuse it”, by dropping all the queries to these domains. For instance, if we want to drop all the DNS queries asking for google.com (and subdomains) we need to add the directive:

local-zone: "google.com" inform_deny.

This will silently drop the DNS query and it will write an entry in the log file (/usr/local/etc/unbound/logs/unbound.log in our case). The client will see show the query times out.

[1472139065] unbound[28162:0] info: google.com. inform 10.10.10.3@31679 google.com. A IN

[1472139071] unbound[28162:0] info: google.com. inform 10.10.10.3@56551 google.com. A IN

To keep a tidy configuration, we will not add this big list of local-zone directives in the main configuration file but we will include a file thanks to the the include directive, that is located in the server section.

server:

....
        include: /usr/local/etc/unbound/blackhole.zone
....

Choosing block lists available in Internet

I am using the following URLs that should be considered safe, with around 23 thousand domains listed.

http://mirror1.malwaredomains.com/files/justdomains
https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist
https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt
http://isc.sans.edu/feeds/suspiciousdomains_Low.txt
http://isc.sans.edu/feeds/suspiciousdomains_Medium.txt
http://isc.sans.edu/feeds/suspiciousdomains_High.txt

Updating the block lists

I’ve written a small shell script that downloads all the lists every night and reloads the Unbound configuration.

Please, notice that reloading Unbound will also flush the DNS cache. A good way to do it is:

Dump the cache

# unbound-control dump_cache > $cache_file

Then, download the files with fetch(1) and regenerate /usr/local/etc/unbound/blackhole.zone

Reload the configuration

# unbound-control reload

Load the cache dump back in Unbound

# unbound-control load_cache < $cache_file

Bonus: use dnscrypt to avoid DNS spoofing

Dnscrypt can be used to avoid some common DNS attacks by encrypting and signing the DNS queries. All traffic will go encrypted using the port 443, both TCP and UDP.

Of course, other issues remain, like DNS spoofing at the server end and the possible logging.

The client is available in the port tree under dns/dnscrypt-proxy and it is really easy to configure. We only need two parameters: the ip:port where we want to listen and which server we want to connect to (aka Resolver)

# sysrc  dnscrypt_proxy_enable="YES"

# sysrc dnscrypt_proxy_flags="-a 10.10.10.10:5353"

# sysrc  dnscrypt_proxy_resolver="dnscrypt.eu-nl"

# service dnscrypt_proxy start

The final step will be configuring Unbound to forward all the DNS queries to dnscrypt. This can be done in the forward-zone section.

forward-zone:

  name: "."

      forward-addr: 10.10.10.10@5353

Final Unbound configuration file

server:

        interface: 10.10.10.10

        access-control: 10.10.10.0/24 allow


        logfile: "/usr/local/etc/unbound/logs/unbound.log"


        username: unbound

        directory: /usr/local/etc/unbound

        chroot: /usr/local/etc/unbound

        pidfile: /usr/local/etc/unbound/unbound.pid

        verbosity: 1


        root-hints: /usr/local/etc/unbound/root.hints

        include: /usr/local/etc/unbound/blackhole.zone


remote-control:

       control-enable: yes

       control-interface: /usr/local/etc/unbound/local_unbound.ctl

       control-use-cert: no


forward-zone:

  name: "."

      forward-addr: 10.10.10.10@5353