HowTo: Jailed unbound > dnscrypt-proxy with DNSSEC

dns/dnscrypt-proxy is an awesome piece of of software that encrypts your DNS traffic using elliptic-curve cryptography. It sends and receives all external queries over port 443 instead of the unencrypted port 53 that is vulnerable to many attack methods.

dnscrypt-proxy is not a DNS cache however, which means incoming queries will not be cached and every single DNS lookup will require a re-query to the upstream resolver. Thus, the recommended way of running dnscrypt-proxy is to place it in front of a local DNS cache. For this we will use dns/unbound.

Mind you, dns/unbound exists both in the ports tree and as a part of base world. It's most probably already on your system unless you have defined WITHOUT_UNBOUND= yes in /etc/src.conf for running your own buildworld. Ports and world usually have the same unbound version so this is mostly a trivial matter, with the exception being that unbound from world uses /var/unbound/, while unbound from ports uses /usr/local/etc/unbound/. My personal opinion is that /var/unbound/ is the more correct location because DNSSEC does key modifications and file ownership is unbound:unbound.

Installation:

Start your DNS jail (I am skipping setup, administration, etc. of jails as this is a separate subject) and install needed packages: # pkg -j dns-jail install dns/dnscrypt-proxy. This should pull in: ports-mgmt/pkg, security/libsodium and dns/dnscrypt-proxy. Repeat the above for dns/unbound if you have decided to use this from ports. At this stage, your jail's /etc/resolv.conf should use your normal DNS IP as nameserver.

Download Some Files:

Configure unbound:

My DNS jail's IP is 192.168.2.97/32 and I have assigned the 192.168.2.96/28 subnet to all the jails I am running on the system. This information is so that you can make sense of the below configuration file.
Code:
server:
  logfile: "/var/log/unbound.log"
  verbosity: 1
  chroot: ""
  pidfile: "/var/run/unbound.pid"

  port: 53    # port to answer queries from
  do-ip4: yes    # Enable IPv4, "yes" or "no".
  do-ip6: no    # Enable IPv6, "yes" or "no".
  do-udp: yes    # Enable UDP, "yes" or "no".
  do-tcp: yes

  hide-identity: yes
  hide-version: yes
  rrset-roundrobin: yes
  minimal-responses: no
  use-caps-for-id: yes
  cache-min-ttl: 60
  prefetch: yes
  prefetch-key: yes
  num-threads: 1  # 1 is enough for not heavy loaded server

  # client ips that are allowed to query to this server.
  access-control: 192.168.1.0/24 allow
  access-control: 192.168.2.96/28 allow
  access-control: 127.0.0.0/24 allow

  # Enforce privacy of these addresses.
  private-address: 192.168.1.0/24
  private-address: 192.168.2.96/28

  root-hints: "/var/unbound/root.hints"

  # You need these to turn on DNSSEC validation
  auto-trust-anchor-file: "/var/unbound/root.key"
  module-config: "validator iterator"

  # You need this as no for dnscrypt-proxy to work
  do-not-query-localhost: no

# Yoyo.org anti-ad server listing with script in dns/root/ad_servers.sh
  include: "/var/unbound/ad_servers"

  forward-zone:
  name: "."
      forward-addr: 8.8.8.8  # Google Public DNS
  #   forward-addr: 192.168.2.97@9053  # dnscrypt-proxy

Other optional settings:
Code:
  # enable remote-control
  remote-control:
  control-enable: yes
  control-interface: 127.0.0.1

  # Optional Hardening knobs:
  harden-glue: yes
  harden-dnssec-stripped: yes
  harden-short-bufsize: yes
  harden-large-queries: yes


Initial DNSSEC Test (not using dnscrypt-proxy yet):

Modify dns-jail/etc/rc.conf as
Code:
local_unbound_enable="YES"
## (if from ports, use) unbound_enable="YES"
dnscrypt_proxy_enable="YES"
dnscrypt_proxy_resolver="dnscrypt.eu-nl"
dnscrypt_proxy_flags="-a 127.0.0.1:9053"
Then,
  • # echo "nameserver 127.0.0.1" > dns-jail/etc/resolv.conf (Set the jail's nameserver.)
  • # echo "nameserver 192.168.2.97" > /etc/resolv.conf (Set the host's nameserver.)
  • Finally, $ drill -D 00f.net should give you a pretty output like below, confirming a functioning DNSSEC setup.
    Code:
    ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 36553
    ;; flags: qr rd ra ad ; QUERY: 1, ANSWER: 2, AUTHORITY: 3, ADDITIONAL: 0
    ;; QUESTION SECTION:
    ;; 00f.net.  IN  A
    
    ;; ANSWER SECTION:
    00f.net.  28729  IN  A  91.121.49.42
    00f.net.  28729  IN  RRSIG  A 7 2 43200 20141129042510 20141101042510 36242 00f.net. KlyemNGKMi5qIUEnUw/BsJ02W2vMjb5gb/LNOy/3s9vF4NEm4SR5cIG0xjKr/0mNCYcp3+roPaWS6lNRu2WAfZxlXm7VLoGO4guQSLD+nlkFnIqMPZAknIYnC7eWW5eaxOVAhgpdKqDfSvSX/4QZ+ddenCkYVrX4B729QhV4jRE=
Switch To dnscrypt-proxy as forward-addr:

Go to the bottom of your var/unbound/unbound.conF, disable the "Google Public DNS" entry and enable the line below that (dnscrypt-proxy). Be aware that:
  • You can change the port that dnscrypt-proxy listens on (in this case 9053) as long as you don't set it to 53 (which is what unbound is listening on).
  • In unbound.conf, you must set "forward-addr as the jail's IP, because setting it as 127.0.0.1@9053" does not work.
  • When this HowTo was written, dnscrypt-proxy had a problem starting up as a service from etc/rc.conf when DNSSEC was enabled. This now seems solved, and specifying the dnscrypt_proxy_resolver flag separately enables the jail to start resolving upon startup.
From the host, $ drill -D 00f.net should now give you same as previously.​

Final Considerations:
  • While dnscrypt-proxy will encrypt all traffic through port 443, port 53 is still used by unbound to fetch and refresh the trust-anchor file.
  • Set the nameserver in all the your jails' etc/resolv.conf to the DNS jail's IP. Alternatively you may choose to force redirection of all port 53 traffic to the DNS jail through settings in pf.conf.

Now go and figure out how to run dnscrypt-proxy on your mobile devices.
 
Last edited:
Extending unbound's capability: Configuring Ad Blocking and periodic root-hints check:

In dns-jail/etc/crontab, add:
Code:
## unbound_ad_server
0  13  *  *  2  root  /root/bin/ad_servers.sh*
## unbound_root.hints
0  13  *  */6  *  root  /root/bin/unb_root_hints.sh*

Under dns-jail/root/bin/, create and set as executable these files:

ad_servers.sh
Code:
#!/bin/sh
# Convert the Yoyo.org anti-ad server listing
# into an unbound dns spoof redirection list.

fetch -o /root/bin/yoyo_ad_servers "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext" && \
cat /root/bin/yoyo_ad_servers | grep 127 | awk '{print $2}' | \
while read line ; \
do \
  echo "local-zone: \"$line\" redirect" ;\
  echo "local-data: \"$line A 127.0.0.1\"" ;\
done > \
/var/unbound/ad_servers

unb_root_hints.sh
Code:
#!/bin/sh
fetch -o /var/unbound/root.hints "ftp://FTP.INTERNIC.NET/domain/named.cache"
 
Big thanks for this great HOWTO!

But, I had to make some more config changes when using FreeBSD 11-Release with local_unbound, i.e. the unbound provided by world, .

Using unbound-checkconf I found that for some reason unknown to me, the "optional hardening knobs" resulted in syntax errors.
Commenting them out did the trick and seems not detrimental, as these hardening settings are the default anyway, according to man unbound.conf.
Furthermore, unbound complained about missing .key and .pem files.
In the web I found a text that told about an unbound-control-setup script, which is also mentioned in the unbound-control manpage.
For some reason this script is not available via the normal search path, however.
A file search suggested me to run sh /usr/src/contrib/unbound/smallapp/unbound-control-setup.sh to have unbound set up a self-signed certificate. Which worked smoothly.
Then I found out using unbound -d that I had to create an unbound logfile first. touch /var/log/unbound.log;chown unbound:unbound /var/log/unbound.log
After that, the error messages finally were written to the logfile.
unbound still refused to start until I changed the control-interface directive from 127.0.0.1 to my jail's IP.
Furthermore I commented out the line including ad_servers file temporarily.
After all those steps, the test run (without DNSSEC) worked fine.

Now, to make DNSSEC work, I did the changes in the second post and had to manually run ad_servers.sh to not wait for the next cron run.
The now-unused [local_]unbound on the jail host can be deactivated in rc.conf.
The appropriate redirect then needs be added in pf.conf to allow local network to use it.

After these steps, all worked great!
 
Back
Top