packages/net/unbound/files/unbound.sh
Eric Luehrsen a7fd69233d 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>
2018-06-12 22:32:54 -04:00

1388 lines
38 KiB
Bash

#!/bin/sh
##############################################################################
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# Copyright (C) 2016 Eric Luehrsen
#
##############################################################################
#
# Unbound is a full featured recursive server with many options. The UCI
# provided tries to simplify and bundle options. This should make Unbound
# easier to deploy. Even light duty routers may resolve recursively instead of
# depending on a stub with the ISP. The UCI also attempts to replicate dnsmasq
# features as used in base LEDE/OpenWrt. If there is a desire for more
# detailed tuning, then manual conf file overrides are also made available.
#
##############################################################################
UNBOUND_B_SLAAC6_MAC=0
UNBOUND_B_DNSSEC=0
UNBOUND_B_DNS64=0
UNBOUND_B_EXT_STATS=0
UNBOUND_B_GATE_NAME=0
UNBOUND_B_HIDE_BIND=1
UNBOUND_B_LOCL_BLCK=0
UNBOUND_B_LOCL_SERV=1
UNBOUND_B_MAN_CONF=0
UNBOUND_B_NTP_BOOT=1
UNBOUND_B_QUERY_MIN=0
UNBOUND_B_QRY_MINST=0
UNBOUND_B_AUTH_ROOT=0
UNBOUND_D_CONTROL=0
UNBOUND_D_DOMAIN_TYPE=static
UNBOUND_D_DHCP_LINK=none
UNBOUND_D_EXTRA_DNS=0
UNBOUND_D_LAN_FQDN=0
UNBOUND_D_PRIV_BLCK=1
UNBOUND_D_PROTOCOL=mixed
UNBOUND_D_RESOURCE=small
UNBOUND_D_RECURSION=passive
UNBOUND_D_WAN_FQDN=0
UNBOUND_IP_DNS64="64:ff9b::/96"
UNBOUND_N_EDNS_SIZE=1280
UNBOUND_N_FWD_PORTS=""
UNBOUND_N_RX_PORT=53
UNBOUND_N_ROOT_AGE=9
UNBOUND_TTL_MIN=120
UNBOUND_TXT_DOMAIN=lan
UNBOUND_TXT_FWD_ZONE=""
UNBOUND_TXT_HOSTNAME=thisrouter
UNBOUND_LIST_FORWARD=""
UNBOUND_LIST_INSECURE=""
##############################################################################
# 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."
##############################################################################
. /lib/functions.sh
. /lib/functions/network.sh
. /usr/lib/unbound/defaults.sh
. /usr/lib/unbound/dnsmasq.sh
. /usr/lib/unbound/iptools.sh
. /usr/lib/unbound/rootzone.sh
##############################################################################
create_interface_dns() {
local cfg="$1"
local ipcommand logint ignore ifname ifdashname
local name names address addresses
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
config_get_bool ignore "$cfg" ignore 0
network_get_device ifname "$cfg"
ifdashname="${ifname//./-}"
ipcommand="ip -o address show $ifname"
addresses=$( $ipcommand | awk '/inet/{sub(/\/.*/,"",$4); print $4}' )
ulaprefix=$( uci_get network.@globals[0].ula_prefix )
host_fqdn="$UNBOUND_TXT_HOSTNAME.$UNBOUND_TXT_DOMAIN"
if_fqdn="$ifdashname.$host_fqdn"
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
else
mode=1
fi
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
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
fe80:*|169.254.*)
echo " # note link address $address"
;;
[1-9a-f]*:*[0-9a-f])
# GA and ULA IP6 for HOST IN AAA records (ip command is robust)
for name in $names ; do
echo " local-data: \"$name. 120 IN AAAA $address\""
done
echo " local-data-ptr: \"$address 120 $mode_ptr\""
;;
[1-9]*.*[0-9])
# Old fashioned HOST IN A records
for name in $names ; do
echo " local-data: \"$name. 120 IN A $address\""
done
echo " local-data-ptr: \"$address 120 $mode_ptr\""
;;
esac
done
echo
} >> $UNBOUND_CONFFILE
elif [ "$mode" -gt 0 ] ; then
{
for address in $addresses ; do
case $address in
fe80:*|169.254.*)
echo " # note link address $address"
;;
"${ulaprefix%%:/*}"*)
# Only this networks ULA and only hostname
echo " local-data: \"$UNBOUND_TXT_HOSTNAME. 120 IN AAAA $address\""
echo " local-data-ptr: \"$address 120 $UNBOUND_TXT_HOSTNAME\""
;;
[1-9]*.*[0-9])
echo " local-data: \"$UNBOUND_TXT_HOSTNAME. 120 IN A $address\""
echo " local-data-ptr: \"$address 120 $UNBOUND_TXT_HOSTNAME\""
;;
esac
done
echo
} >> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_local_zone() {
local target="$1"
local partial domain found
if [ -n "$UNBOUND_LIST_DOMAINS" ] ; then
for domain in $UNBOUND_LIST_DOMAINS ; do
case $target in
*"${domain}")
found=1
break
;;
[A-Za-z0-9]*.[A-Za-z0-9]*)
found=0
;;
*) # no dots
found=1
break
;;
esac
done
else
found=0
fi
if [ $found -eq 0 ] ; then
# New Zone! Bundle local-zones: by first two name tiers "abcd.tld."
partial=$( echo "$target" | awk -F. '{ j=NF ; i=j-1; print $i"."$j }' )
UNBOUND_LIST_DOMAINS="$UNBOUND_LIST_DOMAINS $partial"
echo " local-zone: $partial. transparent" >> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_host_record() {
local cfg="$1"
local ip name
# basefiles dhcp "domain" clause which means host A, AAAA, and PRT record
config_get ip "$cfg" ip
config_get name "$cfg" name
if [ -n "$name" -a -n "$ip" ] ; then
create_local_zone "$name"
{
case $ip in
fe80:*|169.254.*)
echo " # note link address $ip for host $name"
;;
[1-9a-f]*:*[0-9a-f])
echo " local-data: \"$name. 120 IN AAAA $ip\""
echo " local-data-ptr: \"$ip 120 $name\""
;;
[1-9]*.*[0-9])
echo " local-data: \"$name. 120 IN A $ip\""
echo " local-data-ptr: \"$ip 120 $name\""
;;
esac
} >> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_mx_record() {
local cfg="$1"
local domain relay pref
# Insert a static MX record
config_get domain "$cfg" domain
config_get relay "$cfg" relay
config_get pref "$cfg" pref 10
if [ -n "$domain" -a -n "$relay" ] ; then
create_local_zone "$domain"
echo " local-data: \"$domain. 120 IN MX $pref $relay.\"" \
>> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_srv_record() {
local cfg="$1"
local srv target port class weight
# Insert a static SRV record such as SIP server
config_get srv "$cfg" srv
config_get target "$cfg" target
config_get port "$cfg" port
config_get class "$cfg" class 10
config_get weight "$cfg" weight 10
if [ -n "$srv" -a -n "$target" -a -n "$port" ] ; then
create_local_zone "$srv"
echo " local-data: \"$srv. 120 IN SRV $class $weight $port $target.\"" \
>> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_cname_record() {
local cfg="$1"
local cname target
# Insert static CNAME record
config_get cname "$cfg" cname
config_get target "$cfg" target
if [ -n "$cname" -a -n "$target" ] ; then
create_local_zone "$cname"
echo " local-data: \"$cname. 120 IN CNAME $target.\"" >> $UNBOUND_CONFFILE
fi
}
##############################################################################
create_access_control() {
local cfg="$1"
local subnets subnets4 subnets6
local validip4 validip6
network_get_subnets subnets4 "$cfg"
network_get_subnets6 subnets6 "$cfg"
subnets="$subnets4 $subnets6"
if [ -n "$subnets" ] ; then
for subnet in $subnets ; do
validip4=$( valid_subnet4 $subnet )
validip6=$( valid_subnet6 $subnet )
if [ "$validip4" = "ok" -o "$validip6" = "ok" ] ; then
# For each "network" UCI add "access-control:" white list for queries
echo " access-control: $subnet allow" >> $UNBOUND_CONFFILE
fi
done
fi
}
##############################################################################
bundle_domain_forward() {
UNBOUND_LIST_FORWARD="$UNBOUND_LIST_FORWARD $1"
}
##############################################################################
bundle_domain_insecure() {
UNBOUND_LIST_INSECURE="$UNBOUND_LIST_INSECURE $1"
}
##############################################################################
bundle_private_interface() {
local ipcommand ifsubnet ifsubnets ifname validip4
network_get_device ifname $1
if [ -n "$ifname" ] ; then
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][0-9a-f][0-9a-f]:*[0-9a-f])
# Special GLA protection for local block; ULA protected as a catagory
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
fi
}
##############################################################################
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 )
if [ ! -d "$dhcp_dir" ] ; then
# make sure odhcpd has a directory to write (not done itself, yet)
mkdir -p "$dhcp_dir"
fi
fi
if [ -f $UNBOUND_KEYFILE ] ; then
filestuff=$( cat $UNBOUND_KEYFILE )
case "$filestuff" in
*"state=2 [ VALID ]"*)
# Lets not lose RFC 5011 tracking if we don't have to
cp -p $UNBOUND_KEYFILE $UNBOUND_KEYFILE.keep
;;
esac
fi
# Blind copy /etc/ to /var/lib/
mkdir -p $UNBOUND_VARDIR
rm -f $UNBOUND_VARDIR/dhcp_*
touch $UNBOUND_CONFFILE
touch $UNBOUND_SRV_CONF
touch $UNBOUND_EXT_CONF
cp -p /etc/unbound/* $UNBOUND_VARDIR/
if [ ! -f $UNBOUND_HINTFILE ] ; then
if [ -f /usr/share/dns/root.hints ] ; then
# Debian-like package dns-root-data
cp -p /usr/share/dns/root.hints $UNBOUND_HINTFILE
elif [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "default root hints (built in rootservers.net)"
fi
fi
if [ ! -f $UNBOUND_KEYFILE ] ; then
if [ -f /usr/share/dns/root.key ] ; then
# Debian-like package dns-root-data
cp -p /usr/share/dns/root.key $UNBOUND_KEYFILE
elif [ -x $UNBOUND_ANCHOR ] ; then
$UNBOUND_ANCHOR -a $UNBOUND_KEYFILE
elif [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "default trust anchor (built in root DS record)"
fi
fi
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
chown -R unbound:unbound $UNBOUND_VARDIR
chmod 755 $UNBOUND_VARDIR
chmod 644 $UNBOUND_VARDIR/*
if [ -f $UNBOUND_CTLKEY_FILE -o -f $UNBOUND_CTLPEM_FILE \
-o -f $UNBOUND_SRVKEY_FILE -o -f $UNBOUND_SRVPEM_FILE ] ; then
# Keys (some) exist already; do not create new ones
chmod 640 $UNBOUND_CTLKEY_FILE $UNBOUND_CTLPEM_FILE \
$UNBOUND_SRVKEY_FILE $UNBOUND_SRVPEM_FILE
elif [ -x /usr/sbin/unbound-control-setup ] ; then
case "$UNBOUND_D_CONTROL" in
[2-3])
# unbound-control-setup for encrypt opt. 2 and 3, but not 4 "static"
/usr/sbin/unbound-control-setup -d $UNBOUND_VARDIR
chown -R unbound:unbound $UNBOUND_CTLKEY_FILE $UNBOUND_CTLPEM_FILE \
$UNBOUND_SRVKEY_FILE $UNBOUND_SRVPEM_FILE
chmod 640 $UNBOUND_CTLKEY_FILE $UNBOUND_CTLPEM_FILE \
$UNBOUND_SRVKEY_FILE $UNBOUND_SRVPEM_FILE
cp -p $UNBOUND_CTLKEY_FILE /etc/unbound/unbound_control.key
cp -p $UNBOUND_CTLPEM_FILE /etc/unbound/unbound_control.pem
cp -p $UNBOUND_SRVKEY_FILE /etc/unbound/unbound_server.key
cp -p $UNBOUND_SRVPEM_FILE /etc/unbound/unbound_server.pem
;;
esac
fi
}
##############################################################################
unbound_control() {
if [ "$UNBOUND_D_CONTROL" -gt 1 ] ; then
if [ ! -f $UNBOUND_CTLKEY_FILE -o ! -f $UNBOUND_CTLPEM_FILE \
-o ! -f $UNBOUND_SRVKEY_FILE -o ! -f $UNBOUND_SRVPEM_FILE ] ; then
# Key files need to be present; if unbound-control-setup was found, then
# they might have been made during unbound_makedir() above.
UNBOUND_D_CONTROL=0
fi
fi
case "$UNBOUND_D_CONTROL" in
1)
{
# Local Host Only Unencrypted Remote Control
echo "remote-control:"
echo " control-enable: yes"
echo " control-use-cert: no"
echo " control-interface: 127.0.0.1"
echo " control-interface: ::1"
echo
} >> $UNBOUND_CONFFILE
;;
2)
{
# Local Host Only Encrypted Remote Control
echo "remote-control:"
echo " control-enable: yes"
echo " control-use-cert: yes"
echo " control-interface: 127.0.0.1"
echo " control-interface: ::1"
echo " server-key-file: \"$UNBOUND_SRVKEY_FILE\""
echo " server-cert-file: \"$UNBOUND_SRVPEM_FILE\""
echo " control-key-file: \"$UNBOUND_CTLKEY_FILE\""
echo " control-cert-file: \"$UNBOUND_CTLPEM_FILE\""
echo
} >> $UNBOUND_CONFFILE
;;
[3-4])
{
# Network Encrypted Remote Control
# (3) may auto setup and (4) must have static key/pem files
# TODO: add UCI list for interfaces to bind
echo "remote-control:"
echo " control-enable: yes"
echo " control-use-cert: yes"
echo " control-interface: 0.0.0.0"
echo " control-interface: ::0"
echo " server-key-file: \"$UNBOUND_SRVKEY_FILE\""
echo " server-cert-file: \"$UNBOUND_SRVPEM_FILE\""
echo " control-key-file: \"$UNBOUND_CTLKEY_FILE\""
echo " control-cert-file: \"$UNBOUND_CTLPEM_FILE\""
echo
} >> $UNBOUND_CONFFILE
;;
esac
{
# Amend your own extended clauses here like forward zones or disable
# above (local, no encryption) and amend your own remote encrypted control
echo
echo "include: $UNBOUND_EXT_CONF" >> $UNBOUND_CONFFILE
echo
} >> $UNBOUND_CONFFILE
}
##############################################################################
unbound_forward() {
local fdomain fresolver resolvers
# Forward selected domains to the upstream (WAN) stub resolver. This may be
# faster or local pool addresses to ISP service login page. This may keep
# internal organization lookups, well, internal to the organization.
if [ -n "$UNBOUND_LIST_FORWARD" ] ; then
resolvers=$( grep nameserver /tmp/resolv.conf.auto | sed "s/nameserver//g" )
if [ -n "$resolvers" ] ; then
for fdomain in $UNBOUND_LIST_FORWARD ; do
{
echo "forward-zone:"
echo " name: \"$fdomain.\""
for fresolver in $resolvers ; do
echo " forward-addr: $fresolver"
done
echo
} >> $UNBOUND_CONFFILE
done
fi
fi
}
##############################################################################
unbound_auth_root() {
local axfrservers="lax.xfr.dns.icann.org iad.xfr.dns.icann.org"
local httpserver="http://www.internic.net/domain/"
local authzones="root arpa in-addr.arpa ip6.arpa"
local server zone realzone
# Download or AXFR the root and arpa zones to reduce the work needed at
# top level of recursion. If your users will hit many ccTLD or you have
# tracking logs resolving many PTR, then this can speed things up.
# Total size of text in TMPFS could be about 5MB.
if [ "$UNBOUND_B_AUTH_ROOT" -gt 0 ] ; then
for zone in $authzones ; do
if [ "$zone" = "root" ] ; then
realzone="."
else
realzone=$zone
fi
{
echo "auth-zone:"
echo " name: \"$realzone\""
for server in $axfrservers ; do
echo " master: \"$server\""
done
echo " url: \"$httpserver$zone.zone\""
echo " fallback-enabled: yes"
echo " for-downstream: no"
echo " for-upstream: yes"
echo " zonefile: \"$zone.zone\""
echo
} >> $UNBOUND_CONFFILE
done
fi
}
##############################################################################
unbound_conf() {
local rt_mem rt_conn modulestring domain ifsubnet
# Make fresh conf file
echo > $UNBOUND_CONFFILE
{
# Make fresh conf file
echo "# $UNBOUND_CONFFILE generated by UCI $( date )"
echo
echo "server:"
echo " username: unbound"
echo " chroot: \"$UNBOUND_VARDIR\""
echo " directory: \"$UNBOUND_VARDIR\""
echo " pidfile: \"$UNBOUND_PIDFILE\""
echo
# No threading
echo " num-threads: 1"
echo " msg-cache-slabs: 1"
echo " rrset-cache-slabs: 1"
echo " infra-cache-slabs: 1"
echo " key-cache-slabs: 1"
echo
# Interface Wildcard (access contol handled by "option local_service")
echo " interface: 0.0.0.0"
echo " interface: ::0"
echo " outgoing-interface: 0.0.0.0"
echo " outgoing-interface: ::0"
echo
# Logging
echo " use-syslog: yes"
echo " verbosity: 1"
echo " statistics-interval: 0"
echo " statistics-cumulative: no"
} >> $UNBOUND_CONFFILE
if [ "$UNBOUND_B_EXT_STATS" -gt 0 ] ; then
{
# Log More
echo " extended-statistics: yes"
echo
} >> $UNBOUND_CONFFILE
else
{
# Log Less
echo " extended-statistics: no"
echo
} >> $UNBOUND_CONFFILE
fi
case "$UNBOUND_D_PROTOCOL" in
ip4_only)
{
echo " do-ip4: yes"
echo " do-ip6: no"
} >> $UNBOUND_CONFFILE
;;
ip6_only)
{
echo " do-ip4: no"
echo " do-ip6: yes"
} >> $UNBOUND_CONFFILE
;;
ip6_prefer)
{
echo " do-ip4: yes"
echo " do-ip6: yes"
echo " prefer-ip6: yes"
} >> $UNBOUND_CONFFILE
;;
mixed)
{
echo " do-ip4: yes"
echo " do-ip6: yes"
} >> $UNBOUND_CONFFILE
;;
*)
if [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "default protocol configuration"
fi
;;
esac
{
# protocol level tuning
echo " edns-buffer-size: $UNBOUND_N_EDNS_SIZE"
echo " msg-buffer-size: 8192"
echo " port: $UNBOUND_N_RX_PORT"
echo " outgoing-port-permit: 10240-65535"
echo
} >> $UNBOUND_CONFFILE
{
# Other harding and options for an embedded router
echo " harden-short-bufsize: yes"
echo " harden-large-queries: yes"
echo " harden-glue: yes"
echo " harden-below-nxdomain: no"
echo " harden-referral-path: no"
echo " use-caps-for-id: no"
echo
} >> $UNBOUND_CONFFILE
if [ -f "$UNBOUND_HINTFILE" ] ; then
# Optional hints if found
echo " root-hints: \"$UNBOUND_HINTFILE\"" >> $UNBOUND_CONFFILE
fi
if [ "$UNBOUND_B_DNSSEC" -gt 0 -a -f "$UNBOUND_KEYFILE" ] ; then
{
echo " auto-trust-anchor-file: \"$UNBOUND_KEYFILE\""
echo
} >> $UNBOUND_CONFFILE
else
echo >> $UNBOUND_CONFFILE
fi
case "$UNBOUND_D_RESOURCE" in
# Tiny - Unbound's recommended cheap hardware config
tiny) rt_mem=1 ; rt_conn=1 ;;
# Small - Half RRCACHE and open ports
small) rt_mem=8 ; rt_conn=5 ;;
# Medium - Nearly default but with some added balancintg
medium) rt_mem=16 ; rt_conn=10 ;;
# Large - Double medium
large) rt_mem=32 ; rt_conn=10 ;;
# Whatever unbound does
*) rt_mem=0 ; rt_conn=0 ;;
esac
if [ "$rt_mem" -gt 0 ] ; then
{
# Set memory sizing parameters
echo " outgoing-range: $(($rt_conn*64))"
echo " num-queries-per-thread: $(($rt_conn*32))"
echo " outgoing-num-tcp: $(($rt_conn))"
echo " incoming-num-tcp: $(($rt_conn))"
echo " rrset-cache-size: $(($rt_mem*256))k"
echo " msg-cache-size: $(($rt_mem*128))k"
echo " key-cache-size: $(($rt_mem*128))k"
echo " neg-cache-size: $(($rt_mem*64))k"
echo " infra-cache-numhosts: $(($rt_mem*256))"
echo
} >> $UNBOUND_CONFFILE
elif [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "default memory configuration"
fi
# Assembly of module-config: options is tricky; order matters
modulestring="iterator"
if [ "$UNBOUND_B_DNSSEC" -gt 0 ] ; then
if [ ! -f "$UNBOUND_TIMEFILE" -a "$UNBOUND_B_NTP_BOOT" -gt 0 ] ; then
# DNSSEC chicken and egg with getting NTP time
echo " val-override-date: -1" >> $UNBOUND_CONFFILE
fi
{
echo " harden-dnssec-stripped: yes"
echo " val-clean-additional: yes"
echo " ignore-cd-flag: yes"
} >> $UNBOUND_CONFFILE
modulestring="validator $modulestring"
fi
if [ "$UNBOUND_B_DNS64" -gt 0 ] ; then
echo " dns64-prefix: $UNBOUND_IP_DNS64" >> $UNBOUND_CONFFILE
modulestring="dns64 $modulestring"
fi
{
# Print final module string
echo " module-config: \"$modulestring\""
echo
} >> $UNBOUND_CONFFILE
case "$UNBOUND_D_RECURSION" in
passive)
{
# Some query privacy but "strict" will break some servers
if [ "$UNBOUND_B_QRY_MINST" -gt 0 \
-a "$UNBOUND_B_QUERY_MIN" -gt 0 ] ; then
echo " qname-minimisation: yes"
echo " qname-minimisation-strict: yes"
elif [ "$UNBOUND_B_QUERY_MIN" -gt 0 ] ; then
echo " qname-minimisation: yes"
else
echo " qname-minimisation: no"
fi
# Use DNSSEC to quickly understand NXDOMAIN ranges
if [ "$UNBOUND_B_DNSSEC" -gt 0 ] ; then
echo " aggressive-nsec: yes"
echo " prefetch-key: no"
fi
# On demand fetching
echo " prefetch: no"
echo " target-fetch-policy: \"0 0 0 0 0\""
echo
} >> $UNBOUND_CONFFILE
;;
aggressive)
{
# Some query privacy but "strict" will break some servers
if [ "$UNBOUND_B_QRY_MINST" -gt 0 \
-a "$UNBOUND_B_QUERY_MIN" -gt 0 ] ; then
echo " qname-minimisation: yes"
echo " qname-minimisation-strict: yes"
elif [ "$UNBOUND_B_QUERY_MIN" -gt 0 ] ; then
echo " qname-minimisation: yes"
else
echo " qname-minimisation: no"
fi
# Use DNSSEC to quickly understand NXDOMAIN ranges
if [ "$UNBOUND_B_DNSSEC" -gt 0 ] ; then
echo " aggressive-nsec: yes"
echo " prefetch-key: yes"
fi
# Prefetch what can be
echo " prefetch: yes"
echo " target-fetch-policy: \"3 2 1 0 0\""
echo
} >> $UNBOUND_CONFFILE
;;
*)
if [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "default recursion configuration"
fi
;;
esac
{
# Reload records more than 10 hours old
# DNSSEC 5 minute bogus cool down before retry
# Adaptive infrastructure info kept for 15 minutes
echo " cache-min-ttl: $UNBOUND_TTL_MIN"
echo " cache-max-ttl: 36000"
echo " val-bogus-ttl: 300"
echo " infra-host-ttl: 900"
echo
} >> $UNBOUND_CONFFILE
if [ "$UNBOUND_B_HIDE_BIND" -gt 0 ] ; then
{
# Block server id and version DNS TXT records
echo " hide-identity: yes"
echo " hide-version: yes"
echo
} >> $UNBOUND_CONFFILE
fi
if [ "$UNBOUND_D_PRIV_BLCK" -gt 0 ] ; then
{
# Remove _upstream_ or global reponses with private addresses.
# Unbounds own "local zone" and "forward zone" may still use these.
# RFC1918, RFC3927, RFC4291, RFC6598, RFC6890
echo " private-address: 10.0.0.0/8"
echo " private-address: 100.64.0.0/10"
echo " private-address: 169.254.0.0/16"
echo " private-address: 172.16.0.0/12"
echo " private-address: 192.168.0.0/16"
echo " private-address: fc00::/7"
echo " private-address: fe80::/10"
echo
} >> $UNBOUND_CONFFILE
fi
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
echo >> $UNBOUND_CONFFILE
fi
if [ "$UNBOUND_B_LOCL_BLCK" -gt 0 ] ; then
{
# Remove DNS reponses from upstream with loopback IP
# Black hole DNS method for ad blocking, so consider...
echo " private-address: 127.0.0.0/8"
echo " private-address: ::1/128"
echo
} >> $UNBOUND_CONFFILE
fi
if [ -n "$UNBOUND_LIST_INSECURE" ] ; then
for domain in $UNBOUND_LIST_INSECURE ; do
# Except and accept domains without (DNSSEC); work around broken domains
echo " domain-insecure: \"$domain\"" >> $UNBOUND_CONFFILE
done
echo >> $UNBOUND_CONFFILE
fi
}
##############################################################################
unbound_access() {
# TODO: Unbound 1.6.0 added "tags" and "views", so we can add tags to
# each access-control IP block, and then divert access.
# -- "guest" WIFI will not be allowed to see local zone data
# -- "child" LAN can black whole a list of domains to http~deadpixel
if [ "$UNBOUND_B_LOCL_SERV" -gt 0 ] ; then
# Only respond to queries from which this device has an interface.
# Prevent DNS amplification attacks by not responding to the universe.
config_load network
config_foreach create_access_control interface
{
echo " access-control: 127.0.0.0/8 allow"
echo " access-control: ::1/128 allow"
echo " access-control: fe80::/10 allow"
echo
} >> $UNBOUND_CONFFILE
else
{
echo " access-control: 0.0.0.0/0 allow"
echo " access-control: ::0/0 allow"
echo
} >> $UNBOUND_CONFFILE
fi
{
# Amend your own "server:" stuff here
echo " include: $UNBOUND_SRV_CONF"
echo
} >> $UNBOUND_CONFFILE
}
##############################################################################
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
/etc/init.d/adblock enabled && adb_enabled=1 || adb_enabled=0
fi
if [ "$adb_enabled" -gt 0 ] ; then
{
# Pull in your selected openwrt/pacakges/net/adblock generated lists
for adb_file in $UNBOUND_VARDIR/adb_list.* ; do
echo " include: $adb_file"
done
echo
} >> $UNBOUND_CONFFILE
fi
}
##############################################################################
unbound_hostname() {
local ifsubnet ifarpa
if [ -n "$UNBOUND_TXT_DOMAIN" ] ; then
{
# 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
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
if [ "$UNBOUND_D_LAN_FQDN" -gt 0 -o "$UNBOUND_D_WAN_FQDN" -gt 0 ] ; then
config_load dhcp
config_foreach create_interface_dns dhcp
fi
if [ -f "$UNBOUND_DHCP_CONF" ] ; then
{
# Seed DHCP records because dhcp scripts trigger externally
# Incremental Unbound restarts may drop unbound-control add records
echo " include: $UNBOUND_DHCP_CONF"
echo
} >> $UNBOUND_CONFFILE
fi
fi
}
##############################################################################
unbound_records() {
if [ "$UNBOUND_D_EXTRA_DNS" -gt 0 ] ; then
# Parasite from the uci.dhcp.domain clauses
config_load dhcp
config_foreach create_host_record domain
fi
if [ "$UNBOUND_D_EXTRA_DNS" -gt 1 ] ; then
config_foreach create_srv_record srvhost
config_foreach create_mx_record mxhost
fi
if [ "$UNBOUND_D_EXTRA_DNS" -gt 2 ] ; then
config_foreach create_cname_record cname
fi
echo >> $UNBOUND_CONFFILE
}
##############################################################################
unbound_uci() {
local cfg="$1"
local dnsmasqpath hostnm
hostnm=$( uci_get system.@system[0].hostname | awk '{print tolower($0)}' )
UNBOUND_TXT_HOSTNAME=${hostnm:-thisrouter}
config_get_bool UNBOUND_B_SLAAC6_MAC "$cfg" dhcp4_slaac6 0
config_get_bool UNBOUND_B_DNS64 "$cfg" dns64 0
config_get_bool UNBOUND_B_EXT_STATS "$cfg" extended_stats 0
config_get_bool UNBOUND_B_HIDE_BIND "$cfg" hide_binddata 1
config_get_bool UNBOUND_B_LOCL_SERV "$cfg" localservice 1
config_get_bool UNBOUND_B_MAN_CONF "$cfg" manual_conf 0
config_get_bool UNBOUND_B_QUERY_MIN "$cfg" query_minimize 0
config_get_bool UNBOUND_B_QRY_MINST "$cfg" query_min_strict 0
config_get_bool UNBOUND_B_AUTH_ROOT "$cfg" prefetch_root 0
config_get_bool UNBOUND_B_LOCL_BLCK "$cfg" rebind_localhost 0
config_get_bool UNBOUND_B_DNSSEC "$cfg" validator 0
config_get_bool UNBOUND_B_NTP_BOOT "$cfg" validator_ntp 1
config_get UNBOUND_IP_DNS64 "$cfg" dns64_prefix "64:ff9b::/96"
config_get UNBOUND_N_EDNS_SIZE "$cfg" edns_size 1280
config_get UNBOUND_N_RX_PORT "$cfg" listen_port 53
config_get UNBOUND_N_ROOT_AGE "$cfg" root_age 9
config_get UNBOUND_D_CONTROL "$cfg" unbound_control 0
config_get UNBOUND_D_DOMAIN_TYPE "$cfg" domain_type static
config_get UNBOUND_D_DHCP_LINK "$cfg" dhcp_link none
config_get UNBOUND_D_EXTRA_DNS "$cfg" add_extra_dns 0
config_get UNBOUND_D_LAN_FQDN "$cfg" add_local_fqdn 0
config_get UNBOUND_D_PRIV_BLCK "$cfg" rebind_protection 1
config_get UNBOUND_D_PROTOCOL "$cfg" protocol mixed
config_get UNBOUND_D_RECURSION "$cfg" recursion passive
config_get UNBOUND_D_RESOURCE "$cfg" resource small
config_get UNBOUND_D_WAN_FQDN "$cfg" add_wan_fqdn 0
config_get UNBOUND_TTL_MIN "$cfg" ttl_min 120
config_get UNBOUND_TXT_DOMAIN "$cfg" domain lan
config_list_foreach "$cfg" "domain_forward" bundle_domain_forward
config_list_foreach "$cfg" "domain_insecure" bundle_domain_insecure
config_list_foreach "$cfg" "rebind_interface" bundle_private_interface
UNBOUND_LIST_DOMAINS="nowhere $UNBOUND_TXT_DOMAIN"
if [ "$UNBOUND_D_DHCP_LINK" = "none" ] ; then
config_get_bool UNBOUND_B_DNSMASQ "$cfg" dnsmasq_link_dns 0
if [ "$UNBOUND_B_DNSMASQ" -gt 0 ] ; then
UNBOUND_D_DHCP_LINK=dnsmasq
if [ ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "Please use 'dhcp_link' selector instead"
fi
fi
fi
if [ "$UNBOUND_D_DHCP_LINK" = "dnsmasq" ] ; then
if [ ! -x /usr/sbin/dnsmasq -o ! -x /etc/init.d/dnsmasq ] ; then
UNBOUND_D_DHCP_LINK=none
else
/etc/init.d/dnsmasq enabled || UNBOUND_D_DHCP_LINK=none
fi
if [ "$UNBOUND_D_DHCP_LINK" = "none" -a ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "cannot forward to dnsmasq"
fi
fi
if [ "$UNBOUND_D_DHCP_LINK" = "odhcpd" ] ; then
if [ ! -x /usr/sbin/odhcpd -o ! -x /etc/init.d/odhcpd ] ; then
UNBOUND_D_DHCP_LINK=none
else
/etc/init.d/odhcpd enabled || UNBOUND_D_DHCP_LINK=none
fi
if [ "$UNBOUND_D_DHCP_LINK" = "none" -a ! -f "$UNBOUND_TIMEFILE" ] ; then
logger -t unbound -s "cannot receive records from odhcpd"
fi
fi
if [ "$UNBOUND_N_EDNS_SIZE" -lt 512 \
-o 4096 -lt "$UNBOUND_N_EDNS_SIZE" ] ; then
logger -t unbound -s "edns_size exceeds range, using default"
UNBOUND_N_EDNS_SIZE=1280
fi
if [ "$UNBOUND_N_RX_PORT" -ne 53 ] \
&& [ "$UNBOUND_N_RX_PORT" -lt 1024 -o 10240 -lt "$UNBOUND_N_RX_PORT" ] ; then
logger -t unbound -s "privileged port or in 5 digits, using default"
UNBOUND_N_RX_PORT=53
fi
if [ "$UNBOUND_TTL_MIN" -gt 1800 ] ; then
logger -t unbound -s "ttl_min could have had awful side effects, using 300"
UNBOUND_TTL_MIN=300
fi
}
##############################################################################
unbound_resolv_setup() {
if [ "$UNBOUND_N_RX_PORT" != "53" ] ; then
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
# listens on localhost:53, lets assume dnsmasq manages the resolver file.
# TODO:
# really check if dnsmasq runs a local (main) resolver in stead of using
# nslookup that times out when no resolver listens on localhost:53.
return
fi
# unbound is designated to listen on 127.0.0.1#53,
# set resolver file to local.
rm -f /tmp/resolv.conf
{
echo "# /tmp/resolv.conf generated by Unbound UCI $( date )"
echo "nameserver 127.0.0.1"
echo "nameserver ::1"
echo "search $UNBOUND_TXT_DOMAIN."
} > /tmp/resolv.conf
}
##############################################################################
unbound_resolv_teardown() {
case $( cat /tmp/resolv.conf ) in
*"generated by Unbound UCI"*)
# our resolver file, reset to auto resolver file.
rm -f /tmp/resolv.conf
ln -s /tmp/resolv.conf.auto /tmp/resolv.conf
;;
esac
}
##############################################################################
unbound_start() {
config_load unbound
config_foreach unbound_uci unbound
unbound_mkdir
if [ "$UNBOUND_B_MAN_CONF" -eq 0 ] ; then
unbound_conf
unbound_access
unbound_adblock
if [ "$UNBOUND_D_DHCP_LINK" = "dnsmasq" ] ; then
dnsmasq_link
else
unbound_hostname
unbound_records
fi
unbound_forward
unbound_auth_root
unbound_control
fi
unbound_resolv_setup
}
##############################################################################
unbound_stop() {
unbound_resolv_teardown
rootzone_update
}
##############################################################################