unbound basics

stub-zones

I use stub-zones a few places in my own unbound. Mostly for internal-only zones which provide information about some internal/network hosts.

stub-zone:
  name: "spear.home.dayid.org."
  stub-addr: 192.168.0.18 # bind.home.dayid.org // maleah alias for isc_named/bind
          
This is used as all requests for "spear.home.dayid.org" are to be answered by 192.168.0.18 which is an ip alias running on "maleah" (where my unbound and nsd run) for an instance of isc_named/bind that runs there just to interact with dynamic & catalog zones. This is kept at the very bottom of my unbound.conf.

Yes, similar end-results can be accomplished by using a forward-zone, but a forward zone just forwards the request to another server (recursive or otherwise) whereas a stub-zone is pre-loading the cache with static entries for the nameservers of that particular zone.

You can confirm these by using `unbound-control list_stubs`.
$ doas unbound-control list_stubs | grep spear
spear.home.dayid.org. IN stub noprime 192.168.0.18
          
I serve 168.192.in-addr.arpa and 10.in-addr.arpa authoritively from my NSD instance. Then I also allow my domain (and its subdomains) to contain private addresses:
local-zone: "168.192.in-addr.arpa." nodefault
local-zone: "10.in-addr.arpa." nodefault
private-domain: "home.dayid.org"
domain-insecure: "home.dayid.org"
stub-zone:
  name: "10.in-addr.arpa."
  stub-addr: 127.0.0.1@5353 # NSD
stub-zone:
  name: "168.192.in-addr.arpa."
  stub-addr: 127.0.0.1@5353 # NSD
stub-zone:
  name: "home.dayid.org."
  stub-addr: 127.0.0.1@5353 # NSD
          

local-zone/local-data for blocking

As part of my adblock.conf (which is included in my unbound.conf as follows):

include: /var/unbound/etc/adblock.conf
          
...I use local-zone/local-data to block problematic/hazardous stuff in the following pattern (generated by a script using cron)
local-zone: "analytic.shareaholic.com" always_nxdomain
local-data: "analytic.shareaholic.com" 604800 IN SOA adblock adblock ( 0 604800 604800 604800 604800 )"
          
If one of my clients tries to query for this, the dig output shows the SOA above, but also gives the client an NXDOMAIN:
$ dig @192.168.0.17 analytic.shareaholic.com

; <<>> dig 9.10.8-P1 <<>> @192.168.0.17 analytic.shareaholic.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 24652
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;analytic.shareaholic.com.      IN      A

;; AUTHORITY SECTION:
shareaholic.com.        604800  IN      SOA     adblock. adblock. 0 604800 604800 604800 604800

;; Query time: 0 msec
;; SERVER: 192.168.0.17#53(192.168.0.17)
;; WHEN: Mon Apr 10 21:21:01 EDT 2023
;; MSG SIZE  rcvd: 96

          
I like having all of this instead of a just-nxdomain as I like forcing the negative cache as well as overriding the SOA to make it clear I'm blocking the domain (vs it just being a regular NXDOMAIN). Specifically these appear helpful in making some IoT devices not retrying quite as often.

You can confirm these with unbound-control also:
$ doas unbound-control list_local_data | grep analytic.shareaholic.com
analytic.shareaholic.com.      604800  IN      SOA     adblock. adblock. 0 604800 604800 604800 604800
$ doas unbound-control list_local_zones | grep analytic.shareaholic.com
analytic.shareaholic.com. always_nxdomain
          

local-zone for oddball situations

At one point my workstation was using my local DNS, but I needed to forward just a specific zone to my employer's DNS (through VPN). Thus, I overwrote just the NS for the zone so I could retrieve the company-internal-view of it:

local-zone: "example.com." static
local-zone: "example.com. IN NS 10.1.240.1"
          

The cron script

#!/bin/sh
mkdir -p /tmp/unbound-adblock
cd /tmp/unbound-adblock
# get the popular StevenBlack block list, strip it to just hostnames
ftp -o - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | awk /^0.0.0.0/'{ print $2 }' > /tmp/unbound-adblock/stevenblack.ftp
# Treat my own blacklist the same as any other
cp /etc/unbound/blacklist.txt /tmp/unbound-adblock/dayid-blacklist.ftp
# Concatenate the lists together to sort/uniq to avoid duplicates as this will cause unbound to fail upon reload
cat /tmp/unbound-adblock/*.ftp | sort | uniq > /tmp/unbound-adblock/rawmasterlist
awk '{ print "local-zone: \""$1"\" always_nxdomain"; print "local-data: \""$1" 604800 IN SOA adblock adblock ( 0 604800 604800 604800 604800 )\""     }' /tmp/unbound-adblock/rawmasterlist > /tmp/unbound-adblock/adblock.conf
doas /bin/cp /tmp/unbound-adblcok/adblock.conf /var/unbound/etc/
doas /usr/sbin/rcctl reload unbound
        
$ doas grep adblock /etc/doas.conf
# For /usr/local/bin/adblock.sh
permit nopass _adblock cmd /usr/sbin/rcctl args reload unbound
permit nopass _adblock cmd /bin/cp args /tmp/unbound-adblock/adblock.conf /var/unbound/etc/