.. title: Unbound
.. slug: unbound
.. date: 2017-07-09 12:06:55 UTC
.. tags: archlinux, linux, web
.. category: 
.. link: 
.. description: 
.. type: text

I've been running a local DNS resolver for the last decade. I do that for two reasons: first of all, to bypass `censorship and surveillance, <http://pdes-net.org/cobra/posts/dns-on-nonstandard-ports.html>`_ and second, to profit from the essentially instantaneous answers of a local DNS cache. The latter point is becoming increasingly important in the last years, since modern websites tend to invoke links to dozens of other domains all of which have to be resolved. A satisfactory web experience thus requires, first of all, a low-latency connection to the DNS server we use. 

To specify “low”, let's have a look at the typical connections we have at home. With my vanilla ADSL2+, the latency to the fastest DNS servers around amounts to 8 ms for my desktop, which is connected to the router via a GB switch, or 12 ms for all devices connected via WIFI (802.11g). These values are not too bad, but not what I'd associate with “low latency”. At work, for example, we use a dedicated DNS server available in the intranet with a latency of 0.3 ms. Now we're talking.

I have the same speed at home since 2009, when I started using `pdnsd <https://en.wikipedia.org/wiki/Pdnsd>`_. Unfortunately, the development of this caching DNS proxy has stopped 2012. In addition, `DNSSEC <https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions>`_ was initiated 2010 and is now an indispensable part of the modern internet. To keep up with this development, I hence needed a local recursive DNS resolver that not only caches, but also validates. The `article <https://www.heise.de/ct/ausgabe/2017-12-Namensaufloesung-inklusive-Datenschutz-fuers-Heimnetz-3718563.html>`_ in c't 12/2017 about the validating, recursive and caching DNS resolver `Unbound <https://en.wikipedia.org/wiki/Unbound_(DNS_server)>`_ thus came just in time. 

The setup provided by c't applied to Ubuntu, and proved to be incomplete anyway (see the comments at the end of the article). With the help of the `Archwiki <https://wiki.archlinux.org/index.php/Unbound>`_ and `Calomel, <https://calomel.org/unbound_dns.html%E2%80%8E>`_ I came up with the following configuration that works as desired on Archlinux. On Debian or Fedora/CentOS, some of the initial steps may be not be necessary.

We first install unbound

:: 

	pacman -S unbound

and enable the service

:: 

	systemctl enable unbound.service

We need to edit the service unit, and do that by issuing

:: 

	systemctl edit unbound.service
	
to create a `drop-in_snippet. <https://wiki.archlinux.org/index.php/Drop-in_snippet#Drop-in_files>`_ The command above automatically opens your $EDITOR, i.e., in my case vim. The content of the snippet should be:

:: 

	[Service]            
	ExecStartPre=sudo -u unbound /usr/bin/unbound-anchor -a /etc/unbound/root.key 

After saving the file, give it a meaningful name:

:: 

	cd /etc/systemd/system/unbound.service.d
	mv override.conf update_rootkey.conf
	
We can now turn to the configuration of Unbound. Replace the default configuration file /etc/unbound/unbound.conf by a file with the following content: 

:: 

	# Unbound configuration file 
	#
	# See the unbound.conf(5) man page.
	#
	# See /etc/unbound/unbound.conf.example for a commented
	# reference config file.
	#
	# The following line includes additional configuration files from the
	# /etc/unbound/unbound.conf.d directory.
	
	include: "/etc/unbound/unbound.conf.d/*.conf"
	
Next, we create this directory:

:: 

	mkdir /etc/unbound/unbound.conf.d

Let's put the following four files in this directory:

**01_Basic.conf**

:: 

	## Basic configuration
	#
	server:
		interface: ::0
		interface: 0.0.0.0
			access-control: ::1 allow
			access-control: 2001:DB8:: allow
			# Beispiel f. ULA 
			# access-control: fd00:aaaa:bbbb::/64 allow
			access-control: 192.168.178.0/16 allow 
			verbosity: 1
	
	forward-zone:
		  name: "."
		  # hopefully free of censoring and logging, definitely with DNSSEC Support:
		  forward-addr: 194.150.168.168            # dns.as250.net (CCC)
		  forward-addr: 194.95.202.198             # omni.digital.udk-berlin.de (Universität der Künste)
		  forward-addr: 85.214.20.141              # h1768020.stratoserver.net (Digitalcourage e.V.)
		  forward-addr: 80.237.196.2               # dnsc1.dtfh.de (CCC)
		  forward-addr: 194.8.57.12                # ns.n-ix.net (Nürnberger Internet eXchange)
		  forward-addr: 84.200.69.80               # resolver1.ihgip.net (DNS Watch)
		  forward-addr: 84.200.70.40               # resolver2.ihgip.net (DNS Watch)
		  forward-addr: 77.109.148.136             # xiala.net
		  forward-addr: 77.109.148.137             # xiala.net
		  forward-addr: 91.239.100.100             # anycast.censurfridns.dk (UncensoredDNS)
		  forward-addr: 89.233.43.71               # unicast.censurfridns.dk (UncensoredDNS)
		  forward-addr: 213.73.91.35               # dnscache.berlin.ccc.de (CCC)
		  forward-addr: 62.113.203.55              # secondary.server.edv-froehlich.de (OpenNIC)
		  forward-addr: 62.113.203.99              # OpenNIC
	
**02_Advanced.conf**

:: 

	## Advanced configuration
	#
	server:
	verbosity: 1
	do-ip4: yes
	do-ip6: yes
	do-udp: yes
	do-tcp: yes
 
	root-hints: /etc/unbound/root.hints

	auto-trust-anchor-file: /etc/unbound/root.key

	hide-identity: yes
	hide-version: yes
	harden-glue: yes
	harden-dnssec-stripped: yes
	use-caps-for-id: yes

	minimal-responses: yes
	prefetch: yes
	qname-minimisation: yes
	rrset-roundrobin: yes
	use-caps-for-id: yes

	cache-min-ttl: 3600
	cache-max-ttl: 604800

	include: /etc/unbound/adservers
		
**03_DumbFirewalls.conf**  

:: 

	## reduce edns packet size to help big udp packets                                  
	# over dumb firewalls                     
	#                    

	server:              
	edns-buffer-size: 1232                    
	max-udp-size: 1232   
	
**04_Optimize.conf**

:: 

	# Performance optimization
	# `https://www.unbound.net/documentation/howto_optimise.html <https://www.unbound.net/documentation/howto_optimise.html>`_
	server:
			# use all CPUs
			num-threads: 8  
	        
			# power of 2 close to num-threads  
			msg-cache-slabs: 8
			rrset-cache-slabs: 8
			infra-cache-slabs: 8
			key-cache-slabs: 8
	
			# more cache memory, rrset=msg*2
			rrset-cache-size: 200m
			msg-cache-size: 100m
	
			# more outgoing connections
			# depends on number of cores: 1024/cores - 50 
			outgoing-range: 100
	
			# Larger socket buffer.  OS may need config.
			so-rcvbuf: 8m
			so-sndbuf: 8m
	
			# Faster UDP with multithreading (only on Linux).
			so-reuseport: yes

We're almost done now. Two cronjobs in `/etc/cron.weekly <file:///home/cobra/Documents/etc/cron.weekly>`_ complete the configuration:

**unbound_updates**

.. code:: bash

	#!/bin/bash
	# Updating root hints.
	
	###[ root.hints ]###
	
	curl -sS -L --compressed -o /etc/unbound/root.hints.new `https://www.internic.net/domain/named.cache <https://www.internic.net/domain/named.cache>`_
	
	if ` $? -eq 0  <>`_; then
	  mv /etc/unbound/root.hints /etc/unbound/root.hints.bak
	  mv /etc/unbound/root.hints.new /etc/unbound/root.hints
	  unbound-checkconf >/dev/null
	  if ` $? -eq 0  <>`_; then
		rm /etc/unbound/root.hints.bak
		systemctl restart unbound.service
	  else
		echo "Warning: Errors in newly downloaded root hints probably due to incomplete download:"
		unbound-checkconf
		mv /etc/unbound/root.hints /etc/unbound/root.hints.new
		mv /etc/unbound/root.hints.bak /etc/unbound/root.hints
	  fi
	else
	  echo "Download of unbound root.hints failed!"
	fi

**adserver_updates**

.. code:: bash

	#!/bin/bash
	# Updating adserver list.
	
	###[ adservers ]###
	
	curl -sS -L --compressed -o /etc/unbound/adservers.new "`https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext <https://pgl.yoyo.org/adservers/serverlist.php?hostformat=unbound&showintro=0&mimetype=plaintext>`_"
	
	if ` $? -eq 0  <>`_; then
	  mv /etc/unbound/adservers /etc/unbound/adservers.bak
	  mv /etc/unbound/adservers.new /etc/unbound/adservers
	  unbound-checkconf >/dev/null
	  if ` $? -eq 0  <>`_; then
		rm /etc/unbound/adservers.bak
		systemctl restart unbound.service
	  else
		echo "Warning: Errors in newly downloaded adserver list probably due to incomplete download:"
		unbound-checkconf
		mv /etc/unbound/adservers /etc/unbound/adservers.new
		mv /etc/unbound/adservers.bak /etc/unbound/adservers
	  fi
	else
	  echo "Download of unbound adservers failed!"
	fi
	
The adserver component is of course optional, but I've found it to be a very efficient way of blocking ads. I'll compare the various possibilities to block ads in a forthcoming post.

For the moment, let's concentrate on the core competences of our new DNS resolver.  To do so, we first start it by issuing

:: 

	systemctl start unbound.service

We can test the resolver on the command line using either `dig <https://en.wikipedia.org/wiki/Dig_(command)>`_ or its near drop-in replacement `drill <https://wiki.archlinux.org/index.php/DNSSEC>`_.

:: 

	dig +dnssec +multi @localhost debian.org
	drill -D @localhost debian.org

What's essential here are the first two lines and the entries in rcode and flags: 'NOERROR' and 'ad', with the latter standing for 'Authenticated Data'. In other words, the DNS response is authentic because it was validated using DNSSEC. The RRSIG blocks provide, among other data, the public key of the domain as explained `here. <https://tools.ietf.org/html/rfc4034#section-2.3>`_

Let's try that with a domain which is not validated by DNSSEC:

:: 

	dig +dnssec +multi @localhost archlinux.org
	
NOERROR, but no 'ad' flag. Quite all right.

And now a domain with a broken/bogus DNSSEC record:

:: 

	dig +dnssec +multi @localhost dnssec-failed.org
	
Status: SERVFAIL. Works as well. 

Last but no least, let's test the cache of unbound:

:: 

	for i in $(seq 1 5); do dig www.tuvaluislands.com | grep 'Query time' | awk '{print substr($0, index($0, $2))}'; done
	Query time: 746 msec
	Query time: 0 msec
	Query time: 0 msec
	Query time: 0 msec
	Query time: 0 msec

Works. 😉

If the command line appears to be too cryptic, we can also test the basic DNSSEC functionality with a browser:

For addresses with broken/bogus DNSSEC records, such as `this one, <http://www.dnssec-failed.org/>`_ the browser should just display an ERR_NAME_NOT_RESOLVED page. It does? Excellent. 

Still...that page is depressing. Let's boost our morale by visiting `https://dnssec.vs.uni-due.de/ <https://dnssec.vs.uni-due.de/>`_ :

.. image:: ../images/dnssec.png

Thank you, Matthäus 😉 .
