From 9d96dfd4687873bf0bf84d36245c4d86a2db1a0d Mon Sep 17 00:00:00 2001 From: Eric Luehrsen Date: Tue, 9 May 2017 22:46:39 -0400 Subject: [PATCH] unbound: add option for dhcp UCI domain mx srv and cname Base LEDE/OpenWrt UCI for dnsmasq provides for DNS override in /etc/config/dhcp. It is desired to be able to use dnsmasq and Unbound as transparently as possible. Option 'add_extra_dns' will pull 'domain', 'mxhost', 'srvhost, and 'cname' from base. netifd/procd have an interaction with DHCPv6/RA on WAN (FS#713). Minor IP6 parameter updates can cause Unbound reload events every few minutes. List option 'trigger' selects which interfaces may cause reload. For example 'lan', 'wan' but not 'wan6'. Squash other cosmetics. Signed-off-by: Eric Luehrsen --- net/unbound/Makefile | 2 +- net/unbound/files/README.md | 25 +++-- net/unbound/files/odhcpd.sh | 2 +- net/unbound/files/unbound.init | 17 ++- net/unbound/files/unbound.sh | 187 +++++++++++++++++++++++++++++++-- net/unbound/files/unbound.uci | 5 +- 6 files changed, 215 insertions(+), 23 deletions(-) diff --git a/net/unbound/Makefile b/net/unbound/Makefile index 7dcd2a608..a7171e59d 100644 --- a/net/unbound/Makefile +++ b/net/unbound/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=unbound PKG_VERSION:=1.6.2 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_LICENSE:=BSD-3-Clause PKG_LICENSE_FILES:=LICENSE diff --git a/net/unbound/files/README.md b/net/unbound/files/README.md index d8bdf958f..db0f5974f 100644 --- a/net/unbound/files/README.md +++ b/net/unbound/files/README.md @@ -73,7 +73,7 @@ config dhcp 'lan' ``` ### Unbound and odhcpd -You may ask can Unbound replace dnsmasq? You can have DHCP-DNS records with Unbound and odhcpd only. The UCI scripts will allow Unbound to act like dnsmasq. When odhcpd configures each DHCP lease, it will call a script. The script provided with Unbound will read the lease file for DHCP-DNS records. You **must install** `unbound-control`, because the lease records are added and removed without starting, stopping, flushing cache, or re-writing conf files. (_restart overhead can be excessive with even a few mobile devices._) +You may ask, "can Unbound replace dnsmasq?" You can have DHCP-DNS records with Unbound and odhcpd only. The UCI scripts will allow Unbound to act like dnsmasq. When odhcpd configures each DHCP lease, it will call a script. The script provided with Unbound will read the lease file for DHCP-DNS records. You **must install** `unbound-control`, because the lease records are added and removed without starting, stopping, flushing cache, or re-writing conf files. (_restart overhead can be excessive with even a few mobile devices._) Don't forget to disable or uninstall dnsmasq when you don't intend to use it. Strange results may occur. If you want to use default dnsmasq+odhcpd and add Unbound on top, then use the dnsmasq-serial or dnsmasq-parallel methods above. @@ -106,8 +106,7 @@ config dhcp 'lan' option leasetime '12h' option ra 'server' option ra_management '1' - # issue your ULA and avoid default [fe80::] - list dns 'fdxx:xxxx:xxxx::1' + # odhcpd should issue ULA [fd00::/8] by default ... config odhcpd 'odhcpd' @@ -146,6 +145,14 @@ The former will be added to the end of the `server:` clause. The later will be a config unbound Currently only one instance is supported. + option add_extra_dns '0' + Level. Execute traditional DNS overrides found in `/etc/config/dhcp`. + Optional so you may use other Unbound conf or redirect to NSD instance. + 0 - Ignore `/etc/config/dhcp` + 1 - Use only 'domain' clause (host records) + 2 - Use 'domain', 'mxhost', and 'srvhost' clauses + 3 - Use all of 'domain', 'mxhost', 'srvhost', and 'cname' clauses + option add_local_fqdn '0' Level. This puts your routers host name in the LAN (local) DNS. Each level is more detailed and comprehensive. @@ -271,9 +278,15 @@ config unbound embedded devices don't have a real time power off clock. NTP needs DNS to resolve servers. This works around the chicken-and-egg. - list domain_insecure - List. Domains or pointers that you wish to skip DNSSEC. Your DHCP - domains and pointers in dnsmasq will get this automatically. + list domain_insecure 'www.example.com' + Domain. Domains that you wish to skip DNSSEC. Your DHCP + domains and pointers will get this automatically. + + list trigger 'lan' 'wan' + Interface (logical). This option is a work around for netifd/procd + interaction with WAN DHCPv6. Minor RA or DHCP changes in IP6 can + cause netifd to execute procd interface reload. Limit Unbound procd + triggers to LAN and WAN (IP4 only) to prevent restart @2-3 minutes. ``` diff --git a/net/unbound/files/odhcpd.sh b/net/unbound/files/odhcpd.sh index 22a470b4c..c93d280a0 100644 --- a/net/unbound/files/odhcpd.sh +++ b/net/unbound/files/odhcpd.sh @@ -45,7 +45,7 @@ odhcpd_zonedata() { local dhcp_ls_old=$UNBOUND_VARDIR/dhcp_lease.old local dhcp_ls_add=$UNBOUND_VARDIR/dhcp_lease.add local dhcp_ls_del=$UNBOUND_VARDIR/dhcp_lease.del - local dhcp_origin=$( uci get dhcp.@odhcpd[0].leasefile ) + local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile ) config_load unbound config_foreach odhcpd_settings unbound diff --git a/net/unbound/files/unbound.init b/net/unbound/files/unbound.init index 1edf9ef80..5027b79b5 100755 --- a/net/unbound/files/unbound.init +++ b/net/unbound/files/unbound.init @@ -47,7 +47,7 @@ start_service() { stop_service() { unbound_stop - + # Wait! on restart Unbound may take time writing closure stats to syslog pidof $PROG && sleep 1 } @@ -55,9 +55,20 @@ stop_service() { ############################################################################## service_triggers() { - # use soft reload to prevent continuous stop-start and cache flush + local trigger + local triggers=$( uci_get unbound.@unbound[0].trigger ) + + PROCD_RELOAD_DELAY=2000 procd_add_reload_trigger "unbound" - procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/unbound reload + + if [ -n "$triggers" ] ; then + for trigger in $triggers ; do + # due to some netifd/procd interactions with IP6, limit interfaces + procd_add_reload_interface_trigger "$trigger" + done + else + procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/unbound reload + fi } ############################################################################## diff --git a/net/unbound/files/unbound.sh b/net/unbound/files/unbound.sh index 4daefda4d..197a06d97 100644 --- a/net/unbound/files/unbound.sh +++ b/net/unbound/files/unbound.sh @@ -14,9 +14,12 @@ # ############################################################################## # -# This builds the basic UCI components currently supported for Unbound. It is -# intentionally NOT comprehensive and bundles a lot of options. The UCI is to -# be a simpler presentation of the total Unbound conf set. +# 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. # ############################################################################## @@ -36,6 +39,7 @@ UNBOUND_B_QRY_MINST=0 UNBOUND_D_DOMAIN_TYPE=static UNBOUND_D_DHCP_LINK=none +UNBOUND_D_EXTRA_DNS=0 UNBOUND_D_LAN_FQDN=0 UNBOUND_D_PROTOCOL=mixed UNBOUND_D_RESOURCE=small @@ -57,6 +61,11 @@ UNBOUND_TXT_HOSTNAME=thisrouter ############################################################################## +# keep track of local-domain: assignments during inserted resource records +UNBOUND_LIST_DOMAINS="" + +############################################################################## + UNBOUND_LIBDIR=/usr/lib/unbound UNBOUND_VARDIR=/var/lib/unbound @@ -69,7 +78,7 @@ UNBOUND_CONFFILE=$UNBOUND_VARDIR/unbound.conf UNBOUND_KEYFILE=$UNBOUND_VARDIR/root.key UNBOUND_HINTFILE=$UNBOUND_VARDIR/root.hints -UNBOUND_TIMEFILE=$UNBOUND_VARDIR/unbound.time +UNBOUND_TIMEFILE=$UNBOUND_VARDIR/hotplug.time ############################################################################## @@ -124,8 +133,8 @@ create_interface_dns() { ifdashname="${ifname//./-}" ipcommand="ip -o address show $ifname" - addresses="$($ipcommand | awk '/inet/{sub(/\/.*/,"",$4); print $4}')" - ulaprefix="$(uci_get network @globals[0] ula_prefix)" + 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" @@ -223,6 +232,135 @@ create_interface_dns() { ############################################################################## +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 @@ -257,8 +395,8 @@ create_domain_insecure() { unbound_mkdir() { local resolvsym=0 - local dhcp_origin=$( uci get dhcp.@odhcpd[0].leasefile ) - local dhcp_dir=$( dirname "$dhcp_origin" ) + local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile ) + local dhcp_dir=$( dirname $dhcp_origin ) local filestuff @@ -780,11 +918,35 @@ unbound_hostname() { ############################################################################## +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)}')" + 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 @@ -808,6 +970,7 @@ unbound_uci() { 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_PROTOCOL "$cfg" protocol mixed config_get UNBOUND_D_RECURSION "$cfg" recursion passive @@ -817,6 +980,7 @@ unbound_uci() { config_get UNBOUND_TTL_MIN "$cfg" ttl_min 120 config_get UNBOUND_TXT_DOMAIN "$cfg" domain lan + 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 @@ -824,8 +988,8 @@ unbound_uci() { 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 @@ -898,6 +1062,7 @@ unbound_start() { dnsmasq_link else unbound_hostname + unbound_records fi unbound_control diff --git a/net/unbound/files/unbound.uci b/net/unbound/files/unbound.uci index 9e58ac799..45022f408 100644 --- a/net/unbound/files/unbound.uci +++ b/net/unbound/files/unbound.uci @@ -1,4 +1,5 @@ config unbound + option add_extra_dns '0' option add_local_fqdn '1' option add_wan_fqdn '0' option dhcp_link 'none' @@ -24,5 +25,7 @@ config unbound option unbound_control '0' option validator '0' option validator_ntp '1' - #list domain_insecure '' + list trigger 'lan' + list trigger 'wan' + #list domain_insecure 'www.example.com'