unbound: improve local zone evaluation in UCI

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>
This commit is contained in:
Eric Luehrsen 2018-06-10 15:20:24 -04:00
parent d6e7f64a3d
commit a7fd69233d
5 changed files with 190 additions and 78 deletions

View 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

View 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.

View 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
}
##############################################################################

View 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

View 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"
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
mode_ptr="$host_fqdn"
names="$host_fqdn $UNBOUND_TXT_HOSTNAME"
else
mode_ptr="$if_fqdn"
names="$if_fqdn $host_fqdn $UNBOUND_TXT_HOSTNAME"
if [ -z "${ulaprefix%%:/*}" ] ; then
# Nonsense so this option isn't globbed below
ulaprefix="fdno:such:addr::/48"
fi
;;
*)
mode_ptr="$UNBOUND_TXT_HOSTNAME"
names="$UNBOUND_TXT_HOSTNAME"
;;
esac
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"
;;
4)
mode_ptr="$if_fqdn"
names="$if_fqdn $host_fqdn $UNBOUND_TXT_HOSTNAME"
;;
esac
{
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