unbound: improve local zone evaluation in UCI
authorEric Luehrsen <ericluehrsen@hotmail.com>
Sun, 10 Jun 2018 19:20:24 +0000 (15:20 -0400)
committerEric Luehrsen <ericluehrsen@hotmail.com>
Wed, 13 Jun 2018 02:32:54 +0000 (22:32 -0400)
When UCI local zone is private and static, Unbound covered private
addresses with defaults. Optional delegated global IP6 prefix
protection lacked a static zone, but it was prevented from appearing
in global DNS responses. Domain names router-as-TLD, "lan." and
"local." were static, but they lacked default SOA or NS such as
Unbound had assinged to private addresses. Clean up these local
zones UCI evaluation and block global DNS inclusion.

Signed-off-by: Eric Luehrsen <ericluehrsen@hotmail.com>
net/unbound/Makefile
net/unbound/files/README.md
net/unbound/files/iptools.sh
net/unbound/files/odhcpd.sh
net/unbound/files/unbound.sh

index a6de8db33e0c8ec007d22c1f2889c66f9e0da6a4..12144aeb965df3b41cb77f9a34838b40531e7eae 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=unbound
 PKG_VERSION:=1.7.2
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_LICENSE:=BSD-3-Clause
 PKG_LICENSE_FILES:=LICENSE
index 4e81162f8a5902d31a8a6159c939f991f0519a52..d897044380954ee51ef8307eb8d4a6dbf4701852 100644 (file)
@@ -139,7 +139,7 @@ config unbound
 ### Hybrid Manual/UCI
 You like the UCI. Yet, you need to add some difficult to standardize options, or just are not ready to make a UCI request yet. The files `/etc/unbound/unbound_srv.conf` and `/etc/unbound/unbound_ext.conf` will be copied to Unbounds chroot directory and included during auto generation.
 
-The former will be added to the end of the `server:` clause. The later will be added to the end of the file for extended `forward:` and `view:` clauses. You can also disable unbound-control in the UCI which only allows "localhost" connections unencrypted, and then add an encrypted remote `control:` clause.
+The file `unbound_srv.conf` will be added into the `server:` clause. The file `unbound_ext.conf` will be added to the end of all configuration. It is for extended `forward-zone:`, `stub-zone:`, `auth-zone:`, and `view:` clauses. You can also disable unbound-control in the UCI which only allows "localhost" connections unencrypted, and then add an encrypted remote `control:` clause.
 
 ## Complete List of UCI Options
 **/etc/config/unbound**:
@@ -196,8 +196,11 @@ config unbound
 
   option domain_type 'static'
     Unbound local-zone: <domain> <type>. This allows you to lock
-    down or allow forwarding of your domain, your router host name
-    without suffix, and leakage of RFC6762 "local."
+    down or allow forwarding of the local zone. Notable types:
+    static - typical single router setup much like OpenWrt dnsmasq default
+    refuse - to answer overtly with DNS code REFUSED
+    deny - to drop queries for the local zone
+    transparent - to use your manually added forward-zone: or stub-zone: clause
 
   option edns_size '1280'
     Bytes. Extended DNS is necessary for DNSSEC. However, it can run
@@ -226,9 +229,9 @@ config unbound
     configuration. Make changes to /etc/unbound/unbound.conf.
 
   option prefetch_root '0'
-    Boolean. Enable Unbound authority zone clauses for "." (root), "arpa,"
-    "in-addr.arpa," and "ip6.arpa" and obtain complete zone files from public
-    servers using http or AXFR (gTLD are unfortunately not as public).
+    Boolean. Cache the entire root. Enable Unbound `auth-zone:` clauses for
+    "." (root), "arpa," "in-addr.arpa," and "ip6.arpa." Obtain complete zone
+    files from public servers using http or AXFR. (see RFC7706)
 
   option protocol 'mixed'
     Unbound can limit its protocol used for recursive queries.
index 1725242ec47d1e88026891975a2ce6e6776d13c8..9985f76d05aa93a707d5263a7b9692bf979e9c30 100644 (file)
@@ -138,3 +138,25 @@ private_subnet() {
 
 ##############################################################################
 
+domain_ptr_any() {
+  local subnet=$1
+  local arpa validip4 validip6
+
+  validip4=$( valid_subnet4 $subnet )
+  validip6=$( valid_subnet6 $subnet )
+
+
+  if [ "$validip4" = "ok" ] ; then
+    arpa=$( domain_ptr_ip4 "$subnet" )
+  elif [ "$validip6" = "ok" ] ; then
+    arpa=$( domain_ptr_ip6 "$subnet" )
+  fi
+
+
+  if [ -n "$arpa" ] ; then
+    echo $arpa
+  fi
+}
+
+##############################################################################
+
index 9c01dc6f6eee066c765b25db4cb2c0e5a41ed1e0..93efa73ad15a6870b3eed485013fb217e3944d12 100644 (file)
@@ -43,7 +43,9 @@ odhcpd_zonedata() {
   local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile )
 
 
-  if [ "$dhcp_link" = "odhcpd" -a -f "$dhcp_origin" ] ; then
+  if [ "$dhcp_link" = "odhcpd" \
+      -a -f "$dhcp_origin" \
+      -a -n "$dhcp_domain" ] ; then
     # Capture the lease file which could be changing often
     sort $dhcp_origin > $dhcp_ls_new
 
index 2fda84e8690c8f806ae1522f4d195711a46474fb..a2211775151a14feefbc4b85ff616aa98154781b 100644 (file)
@@ -63,12 +63,18 @@ UNBOUND_TXT_HOSTNAME=thisrouter
 
 UNBOUND_LIST_FORWARD=""
 UNBOUND_LIST_INSECURE=""
-UNBOUND_LIST_PRV_SUBNET=""
 
 ##############################################################################
 
-# keep track of local-domain: assignments during inserted resource records
+# keep track of assignments during inserted resource records
 UNBOUND_LIST_DOMAINS=""
+UNBOUND_LIST_IFACE=""
+UNBOUND_LIST_PRV_IP6GLA=""
+UNBOUND_LIST_LAN_NET=""
+
+# Similar default SOA / NS RR as Unbound uses for private ARPA zones
+UNBOUND_XSOA="3600 IN SOA localhost. nobody.invalid. 1 3600 1200 7200 600"
+UNBOUND_XNS="3600 IN NS localhost."
 
 ##############################################################################
 
@@ -82,34 +88,13 @@ UNBOUND_LIST_DOMAINS=""
 
 ##############################################################################
 
-copy_dash_update() {
-  # TODO: remove this function and use builtins when this issues is resovled.
-  # Due to OpenWrt/LEDE divergence "cp -u" isn't yet universally available.
-  local filetime keeptime
-
-
-  if [ -f $UNBOUND_KEYFILE.keep ] ; then
-    # root.key.keep is reused if newest
-    filetime=$( date -r $UNBOUND_KEYFILE +%s )
-    keeptime=$( date -r $UNBOUND_KEYFILE.keep +%s )
-
-
-    if [ $keeptime -gt $filetime ] ; then
-      cp $UNBOUND_KEYFILE.keep $UNBOUND_KEYFILE
-    fi
-
-
-    rm -f $UNBOUND_KEYFILE.keep
-  fi
-}
-
-##############################################################################
-
 create_interface_dns() {
   local cfg="$1"
   local ipcommand logint ignore ifname ifdashname
   local name names address addresses
-  local ulaprefix if_fqdn host_fqdn mode mode_ptr
+  local ulaprefix if_fqdn host_fqdn
+  local mode_ptr="$UNBOUND_TXT_HOSTNAME"
+  local names="$UNBOUND_TXT_HOSTNAME"
 
   # Create local-data: references for this hosts interfaces (router).
   config_get logint "$cfg" interface
@@ -124,45 +109,60 @@ create_interface_dns() {
   if_fqdn="$ifdashname.$host_fqdn"
 
 
-  if [ -z "${ulaprefix%%:/*}" ] ; then
-    # Nonsense so this option isn't globbed below
-    ulaprefix="fdno:such:addr::/48"
-  fi
+  if [ -z "$ifdashname" ] ; then
+    # race conditions at init can rarely cause a blank device return
+    # the record format is invalid and Unbound won't load the conf file
+    mode=0
 
+  elif [ -n "$UNBOUND_LIST_IFACE" ] ; then
+    case "$UNBOUND_LIST_IFACE" in
+    *$ifdashname*)
+      # repeat such as dual WAN (eth0-1) and WAN6 (eth0-1)
+      mode=0
+      ;;
+
+    *)
+      mode=1
+      ;;
+    esac
 
-  if [ "$ignore" -gt 0 ] ; then
-    mode="$UNBOUND_D_WAN_FQDN"
   else
-    mode="$UNBOUND_D_LAN_FQDN"
+    mode=1
   fi
 
 
-  case "$mode" in
-  3)
-    mode_ptr="$host_fqdn"
-    names="$host_fqdn  $UNBOUND_TXT_HOSTNAME"
-    ;;
+  if [ $mode -gt 0 ] ; then
+    UNBOUND_LIST_IFACE="$UNBOUND_LIST_IFACE $ifdashname"
+
+
+    if [ -z "${ulaprefix%%:/*}" ] ; then
+      # Nonsense so this option isn't globbed below
+      ulaprefix="fdno:such:addr::/48"
+    fi
+
 
-  4)
-    if [ -z "$ifdashname" ] ; then
-      # race conditions at init can rarely cause a blank device return
-      # the record format is invalid and Unbound won't load the conf file
+    if [ "$ignore" -gt 0 ] ; then
+      mode="$UNBOUND_D_WAN_FQDN"
+    else
+      mode="$UNBOUND_D_LAN_FQDN"
+    fi
+  fi
+
+
+  if [ "$mode" -gt 1 ] ; then
+    case "$mode" in
+    3)
       mode_ptr="$host_fqdn"
       names="$host_fqdn  $UNBOUND_TXT_HOSTNAME"
-    else
+      ;;
+
+    4)
       mode_ptr="$if_fqdn"
       names="$if_fqdn  $host_fqdn  $UNBOUND_TXT_HOSTNAME"
-    fi
-    ;;
-
-  *)
-    mode_ptr="$UNBOUND_TXT_HOSTNAME"
-    names="$UNBOUND_TXT_HOSTNAME"
-    ;;
-  esac
+      ;;
+    esac
 
 
-  if [ "$mode" -gt 1 ] ; then
     {
       for address in $addresses ; do
         case $address in
@@ -385,21 +385,37 @@ bundle_domain_insecure() {
 ##############################################################################
 
 bundle_private_interface() {
-  local ipcommand ifsubnet ifsubnets ifname
+  local ipcommand ifsubnet ifsubnets ifname validip4
 
   network_get_device ifname $1
 
+
   if [ -n "$ifname" ] ; then
-    ipcommand="ip -6 -o address show $ifname"
-    ifsubnets=$( $ipcommand | awk '/inet6/{ print $4 }' )
+    ipcommand="ip -o address show $ifname"
+    ifsubnets=$( $ipcommand | awk '/inet/{ print $4 }' )
 
 
     if [ -n "$ifsubnets" ] ; then
       for ifsubnet in $ifsubnets ; do
         case $ifsubnet in
-        [1-9]*:*[0-9a-f])
+        [1-9][0-9a-f][0-9a-f][0-9a-f]:*[0-9a-f])
           # Special GLA protection for local block; ULA protected as a catagory
-          UNBOUND_LIST_PRV_SUBNET="$UNBOUND_LIST_PRV_SUBNET $ifsubnet" ;;
+          UNBOUND_LIST_PRV_IP6GLA="$UNBOUND_LIST_PRV_IP6GLA $ifsubnet"
+          ;;
+
+        f[dc][0-9a-f][0-9a-f]:*[0-9a-f])
+          # Used to configure specific local-zone: data
+          UNBOUND_LIST_LAN_NET="$UNBOUND_LIST_LAN_NET $ifsubnet"
+          ;;
+
+        *)
+          validip4=$( valid_subnet4 $ifsubnet )
+
+
+          if [ "$validip4" = "ok" ] ; then
+            UNBOUND_LIST_LAN_NET="$UNBOUND_LIST_LAN_NET $ifsubnet"
+          fi
+          ;;
         esac
       done
     fi
@@ -411,6 +427,7 @@ bundle_private_interface() {
 unbound_mkdir() {
   local filestuff
 
+
   if [ "$UNBOUND_D_DHCP_LINK" = "odhcpd" ] ; then
     local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile )
     local dhcp_dir=$( dirname $dhcp_origin )
@@ -422,6 +439,7 @@ unbound_mkdir() {
     fi
   fi
 
+
   if [ -f $UNBOUND_KEYFILE ] ; then
     filestuff=$( cat $UNBOUND_KEYFILE )
 
@@ -469,7 +487,11 @@ unbound_mkdir() {
   fi
 
 
-  copy_dash_update
+  if [ -f $UNBOUND_KEYFILE.keep ] ; then
+    # root.key.keep is reused if newest
+    cp -u $UNBOUND_KEYFILE.keep $UNBOUND_KEYFILE
+    rm -f $UNBOUND_KEYFILE.keep
+  fi
 
 
   # Ensure access and prepare to jail
@@ -809,6 +831,7 @@ unbound_conf() {
     logger -t unbound -s "default memory configuration"
   fi
 
+
   # Assembly of module-config: options is tricky; order matters
   modulestring="iterator"
 
@@ -941,8 +964,8 @@ unbound_conf() {
   fi
 
 
-  if  [ -n "$UNBOUND_LIST_PRV_SUBNET" -a "$UNBOUND_D_PRIV_BLCK" -gt 1 ] ; then
-    for ifsubnet in $UNBOUND_LIST_PRV_SUBNET ; do
+  if  [ -n "$UNBOUND_LIST_PRV_IP6GLA" -a "$UNBOUND_D_PRIV_BLCK" -gt 1 ] ; then
+    for ifsubnet in $UNBOUND_LIST_PRV_IP6GLA ; do
       # Remove global DNS responses with your local network IP6 GLA
       echo "  private-address: $ifsubnet" >> $UNBOUND_CONFFILE
     done
@@ -1019,6 +1042,7 @@ unbound_adblock() {
   # TODO: Unbound 1.6.0 added "tags" and "views"; lets work with adblock team
   local adb_enabled adb_file
 
+
   if [ ! -x /usr/bin/adblock.sh -o ! -x /etc/init.d/adblock ] ; then
     adb_enabled=0
   else
@@ -1040,31 +1064,90 @@ unbound_adblock() {
 ##############################################################################
 
 unbound_hostname() {
+  local ifsubnet ifarpa
+
+
   if [ -n "$UNBOUND_TXT_DOMAIN" ] ; then
     {
-      # TODO: Unbound 1.6.0 added "tags" and "views" and we could make
-      # domains by interface to prevent DNS from "guest" to "home"
-      echo "  local-zone: $UNBOUND_TXT_DOMAIN. $UNBOUND_D_DOMAIN_TYPE"
-      echo "  domain-insecure: $UNBOUND_TXT_DOMAIN"
-      echo "  private-domain: $UNBOUND_TXT_DOMAIN"
-      echo
-      echo "  local-zone: $UNBOUND_TXT_HOSTNAME. $UNBOUND_D_DOMAIN_TYPE"
+      # Hostname as TLD works, but not transparent through recursion
       echo "  domain-insecure: $UNBOUND_TXT_HOSTNAME"
       echo "  private-domain: $UNBOUND_TXT_HOSTNAME"
+      echo "  local-zone: $UNBOUND_TXT_HOSTNAME. static"
+      echo "  local-data: \"$UNBOUND_TXT_HOSTNAME. $UNBOUND_XSOA\""
+      echo "  local-data: \"$UNBOUND_TXT_HOSTNAME. $UNBOUND_XNS\""
       echo
     } >> $UNBOUND_CONFFILE
 
 
     case "$UNBOUND_D_DOMAIN_TYPE" in
     deny|inform_deny|refuse|static)
+      if  [ -n "$UNBOUND_LIST_PRV_IP6GLA" \
+            -a "$UNBOUND_D_PRIV_BLCK" -gt 1 ] ; then
+        for ifsubnet in $UNBOUND_LIST_PRV_IP6GLA ; do
+          ifarpa=$( domain_ptr_any "$ifsubnet" )
+
+
+          if [ -n "$ifarpa" ] ; then
+            {
+              # Do NOT forward queries with your GLA ip6.arpa
+              echo "  domain-insecure: $ifarpa"
+              echo "  local-zone: $ifarpa. $UNBOUND_D_DOMAIN_TYPE"
+              echo "  local-data: \"$ifarpa. $UNBOUND_XSOA\""
+              echo "  local-data: \"$ifarpa. $UNBOUND_XNS\""
+              echo
+            } >> $UNBOUND_CONFFILE
+          fi
+        done
+      fi
+
+
+      if  [ -n "$UNBOUND_LIST_LAN_NET" \
+            -a "$UNBOUND_D_PRIV_BLCK" -gt 0 ] ; then
+        for ifsubnet in $UNBOUND_LIST_LAN_NET ; do
+          ifarpa=$( domain_ptr_any "$ifsubnet" )
+
+
+          if [ -n "$ifarpa" ] ; then
+            {
+              # Do NOT forward queries with your ULA ip6.arpa or in-addr.arpa
+              echo "  domain-insecure: $ifarpa"
+              echo "  local-zone: $ifarpa. $UNBOUND_D_DOMAIN_TYPE"
+              echo "  local-data: \"$ifarpa. $UNBOUND_XSOA\""
+              echo "  local-data: \"$ifarpa. $UNBOUND_XNS\""
+              echo
+            } >> $UNBOUND_CONFFILE
+          fi
+        done
+      fi
+
+
       {
-        # avoid upstream involvement in RFC6762 like responses (link only)
-        echo "  local-zone: local. $UNBOUND_D_DOMAIN_TYPE"
+        # avoid upstream involvement in RFC6762
         echo "  domain-insecure: local"
         echo "  private-domain: local"
+        echo "  local-zone: local. $UNBOUND_D_DOMAIN_TYPE"
+        echo "  local-data: \"local. $UNBOUND_XSOA\""
+        echo "  local-data: \"local. $UNBOUND_XNS\""
+        echo "  local-data: \"local. 3600 IN TXT RFC6762\""
+        echo
+        # type static means only this router has your domain
+        # type transparent will permit forward-zone: or stub-zone: clauses
+        echo "  domain-insecure: $UNBOUND_TXT_DOMAIN"
+        echo "  private-domain: $UNBOUND_TXT_DOMAIN"
+        echo "  local-zone: $UNBOUND_TXT_DOMAIN. $UNBOUND_D_DOMAIN_TYPE"
+        echo "  local-data: \"$UNBOUND_TXT_DOMAIN. $UNBOUND_XSOA\""
+        echo "  local-data: \"$UNBOUND_TXT_DOMAIN. $UNBOUND_XNS\""
         echo
       } >> $UNBOUND_CONFFILE
       ;;
+
+    *)
+      # likely transparent domain with fordward-zone: clause to next router
+      echo "  domain-insecure: $UNBOUND_TXT_DOMAIN"
+      echo "  private-domain: $UNBOUND_TXT_DOMAIN"
+      echo "  local-zone: $UNBOUND_TXT_DOMAIN. $UNBOUND_D_DOMAIN_TYPE"
+      echo
+      ;;
     esac
 
 
@@ -1227,6 +1310,7 @@ unbound_resolv_setup() {
     return
   fi
 
+
   if [ -x /etc/init.d/dnsmasq ] && /etc/init.d/dnsmasq enabled \
   && nslookup localhost 127.0.0.1#53 >/dev/null 2>&1 ; then
     # unbound is configured for port 53, but dnsmasq is enabled and a resolver
@@ -1237,6 +1321,7 @@ unbound_resolv_setup() {
     return
   fi
 
+
   # unbound is designated to listen on 127.0.0.1#53,
   #   set resolver file to local.
   rm -f /tmp/resolv.conf