#!/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. # ############################################################################## # while useful (sh)ellcheck is pedantic and noisy # shellcheck disable=1091,2002,2004,2034,2039,2086,2094,2140,2154,2155 UB_B_AUTH_ROOT=0 UB_B_DNS_ASSIST=0 UB_B_DNSSEC=0 UB_B_DNS64=0 UB_B_EXT_STATS=0 UB_B_GATE_NAME=0 UB_B_HIDE_BIND=1 UB_B_IF_AUTO=1 UB_B_LOCL_BLCK=0 UB_B_LOCL_SERV=1 UB_B_MAN_CONF=0 UB_B_NTP_BOOT=1 UB_B_QUERY_MIN=0 UB_B_QRY_MINST=0 UB_B_SLAAC6_MAC=0 UB_D_CONTROL=0 UB_D_DOMAIN_TYPE=static UB_D_DHCP_LINK=none UB_D_EXTRA_DNS=0 UB_D_LAN_FQDN=0 UB_D_PRIV_BLCK=1 UB_D_PROTOCOL=mixed UB_D_RESOURCE=small UB_D_RECURSION=passive UB_D_VERBOSE=1 UB_D_WAN_FQDN=0 UB_IP_DNS64="64:ff9b::/96" UB_N_EDNS_SIZE=1232 UB_N_RX_PORT=53 UB_N_ROOT_AGE=9 UB_N_THREADS=1 UB_N_RATE_LMT=0 UB_TTL_MIN=120 UB_TXT_DOMAIN=lan UB_TXT_HOSTNAME=thisrouter ############################################################################## # reset as a combo with UB_B_NTP_BOOT and some time stamp files UB_B_READY=1 # keep track of assignments during inserted resource records UB_LIST_NETW_ALL="" UB_LIST_NETW_LAN="" UB_LIST_NETW_WAN="" UB_LIST_INSECURE="" UB_LIST_ZONE_SERVERS="" UB_LIST_ZONE_NAMES="" ############################################################################## . /lib/functions.sh . /lib/functions/network.sh . /usr/lib/unbound/defaults.sh . /usr/lib/unbound/dnsmasq.sh . /usr/lib/unbound/iptools.sh ############################################################################## bundle_all_networks() { local cfg="$1" local ifname ifdashname validip local subnet subnets subnets4 subnets6 network_get_subnets subnets4 "$cfg" network_get_subnets6 subnets6 "$cfg" network_get_device ifname "$cfg" ifdashname="${ifname//./-}" subnets="$subnets4 $subnets6" if [ -n "$subnets" ] ; then for subnet in $subnets ; do validip=$( valid_subnet_any $subnet ) if [ "$validip" = "ok" ] ; then UB_LIST_NETW_ALL="$UB_LIST_NETW_ALL $ifdashname@$subnet" fi done fi } ############################################################################## bundle_dhcp_networks() { local cfg="$1" local interface ifsubnet ifname ifdashname ignore config_get_bool ignore "$cfg" ignore 0 config_get interface "$cfg" interface "" network_get_device ifname "$interface" ifdashname="${ifname//./-}" if [ $ignore -eq 0 ] && [ -n "$ifdashname" ] \ && [ -n "$UB_LIST_NETW_ALL" ] ; then for ifsubnet in $UB_LIST_NETW_ALL ; do case $ifsubnet in "${ifdashname}"@*) # Special GLA protection for local block; ULA protected default UB_LIST_NETW_LAN="$UB_LIST_NETW_LAN $ifsubnet" ;; esac done fi } ############################################################################## bundle_lan_networks() { local interface="$1" local ifsubnet ifname ifdashname network_get_device ifname "$interface" ifdashname="${ifname//./-}" if [ -n "$ifdashname" ] && [ -n "$UB_LIST_NETW_ALL" ] ; then for ifsubnet in $UB_LIST_NETW_ALL ; do case $ifsubnet in "${ifdashname}"@*) # Special GLA protection for local block; ULA protected default UB_LIST_NETW_LAN="$UB_LIST_NETW_LAN $ifsubnet" ;; esac done fi } ############################################################################## bundle_wan_networks() { local interface="$1" local ifsubnet ifname ifdashname network_get_device ifname "$interface" ifdashname="${ifname//./-}" if [ -n "$ifdashname" ] && [ -n "$UB_LIST_NETW_ALL" ] ; then for ifsubnet in $UB_LIST_NETW_ALL ; do case $UB_LIST_NETW_LAN in *"${ifsubnet}"*) # If LAN, then not WAN ... scripts might become complex ;; *) case $ifsubnet in "${ifdashname}"@*) # Special GLA protection for local block; ULA protected default UB_LIST_NETW_WAN="$UB_LIST_NETW_WAN $ifsubnet" ;; esac ;; esac done fi } ############################################################################## bundle_resolv_conf_servers() { local resolvers=$( awk '/nameserver/ { print $2 }' $UB_RESOLV_AUTO ) UB_LIST_ZONE_SERVERS="$UB_LIST_ZONE_SERVERS $resolvers" } ############################################################################## bundle_zone_names() { UB_LIST_ZONE_NAMES="$UB_LIST_ZONE_NAMES $1" } ############################################################################## bundle_zone_servers() { UB_LIST_ZONE_SERVERS="$UB_LIST_ZONE_SERVERS $1" } ############################################################################## bundle_domain_insecure() { UB_LIST_INSECURE="$UB_LIST_INSECURE $1" } ############################################################################## unbound_mkdir() { local filestuff if [ "$UB_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 $UB_RKEY_FILE ] ; then filestuff=$( cat $UB_RKEY_FILE ) case "$filestuff" in *"state=2 [ VALID ]"*) # Lets not lose RFC 5011 tracking if we don't have to cp -p $UB_RKEY_FILE $UB_RKEY_FILE.keep ;; esac fi # Blind copy /etc/unbound to /var/lib/unbound mkdir -p $UB_VARDIR rm -f $UB_VARDIR/dhcp_* touch $UB_TOTAL_CONF cp -p $UB_ETCDIR/*.conf $UB_VARDIR/ cp -p $UB_ETCDIR/root.* $UB_VARDIR/ if [ ! -f $UB_RHINT_FILE ] ; then if [ -f /usr/share/dns/root.hints ] ; then # Debian-like package dns-root-data cp -p /usr/share/dns/root.hints $UB_RHINT_FILE elif [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "default root hints (built in root-servers.net)" fi fi if [ ! -f $UB_RKEY_FILE ] ; then if [ -f /usr/share/dns/root.key ] ; then # Debian-like package dns-root-data cp -p /usr/share/dns/root.key $UB_RKEY_FILE elif [ -x $UB_ANCHOR ] ; then $UB_ANCHOR -a $UB_RKEY_FILE elif [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "default trust anchor (built in root DS record)" fi fi if [ -f $UB_RKEY_FILE.keep ] ; then # root.key.keep is reused if newest cp -u $UB_RKEY_FILE.keep $UB_RKEY_FILE rm -f $UB_RKEY_FILE.keep fi # Ensure access and prepare to jail chown -R unbound:unbound $UB_VARDIR chmod 755 $UB_VARDIR chmod 644 $UB_VARDIR/* if [ -x /usr/sbin/unbound-control-setup ] ; then if [ ! -f $UB_CTLKEY_FILE ] || [ ! -f $UB_CTLPEM_FILE ] \ || [ ! -f $UB_SRVKEY_FILE ] || [ ! -f $UB_SRVPEM_FILE ] ; then case "$UB_D_CONTROL" in [2-3]) # unbound-control-setup for encrypt opt. 2 and 3, but not 4 "static" /usr/sbin/unbound-control-setup -d $UB_ETCDIR chown -R unbound:unbound $UB_CTLKEY_FILE $UB_CTLPEM_FILE \ $UB_SRVKEY_FILE $UB_SRVPEM_FILE chmod 640 $UB_CTLKEY_FILE $UB_CTLPEM_FILE \ $UB_SRVKEY_FILE $UB_SRVPEM_FILE ;; esac fi fi if [ -f "$UB_TIME_FILE" ] ; then # NTP is done so its like you actually had an RTC UB_B_READY=1 UB_B_NTP_BOOT=0 elif [ $UB_B_NTP_BOOT -eq 0 ] ; then # time is considered okay on this device (ignore /etc/hotplug/ntpd/unbound) date -Is > $UB_TIME_FILE UB_B_READY=0 UB_B_NTP_BOOT=0 else # DNSSEC-TIME will not reconcile UB_B_READY=0 UB_B_NTP_BOOT=1 fi } ############################################################################## unbound_control() { echo "# $UB_CTRL_CONF generated by UCI $( date -Is )" > $UB_CTRL_CONF if [ $UB_D_CONTROL -gt 1 ] ; then if [ ! -f $UB_CTLKEY_FILE ] || [ ! -f $UB_CTLPEM_FILE ] \ || [ ! -f $UB_SRVKEY_FILE ] || [ ! -f $UB_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. UB_D_CONTROL=0 fi fi case "$UB_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 } >> $UB_CTRL_CONF ;; 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: $UB_SRVKEY_FILE" echo " server-cert-file: $UB_SRVPEM_FILE" echo " control-key-file: $UB_CTLKEY_FILE" echo " control-cert-file: $UB_CTLPEM_FILE" echo } >> $UB_CTRL_CONF ;; [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: $UB_SRVKEY_FILE" echo " server-cert-file: $UB_SRVPEM_FILE" echo " control-key-file: $UB_CTLKEY_FILE" echo " control-cert-file: $UB_CTLPEM_FILE" echo } >> $UB_CTRL_CONF ;; esac } ############################################################################## unbound_zone() { local cfg=$1 local servers_ip="" local servers_host="" local zone_sym zone_name zone_type zone_enabled zone_file local tls_upstream fallback local server port tls_port tls_index tls_suffix url_dir dns_ast if [ ! -f "$UB_ZONE_CONF" ] ; then echo "# $UB_ZONE_CONF generated by UCI $( date -Is )" > $UB_ZONE_CONF fi config_get_bool zone_enabled "$cfg" enabled 0 if [ $zone_enabled -eq 1 ] ; then # these lists are built for each zone; empty to start UB_LIST_ZONE_NAMES="" UB_LIST_ZONE_SERVERS="" config_get zone_type "$cfg" zone_type "" config_get port "$cfg" port "" config_get tls_index "$cfg" tls_index "" config_get tls_port "$cfg" tls_port 853 config_get url_dir "$cfg" url_dir "" config_get dns_ast "$cfg" dns_assist none config_get_bool resolv_conf "$cfg" resolv_conf 0 config_get_bool fallback "$cfg" fallback 1 config_get_bool tls_upstream "$cfg" tls_upstream 0 config_list_foreach "$cfg" zone_name bundle_zone_names config_list_foreach "$cfg" server bundle_zone_servers # string formating for Unbound syntax tls_suffix="${tls_port:+@${tls_port}${tls_index:+#${tls_index}}}" [ $fallback -eq 0 ] && fallback=no || fallback=yes [ $tls_upstream -eq 0 ] && tls_upstream=no || tls_upstream=yes if [ $resolv_conf -eq 1 ] ; then bundle_resolv_conf_servers fi else zone_type=skip fi case "$dns_ast" in bind) if [ -x /usr/sbin/bind ] && [ -x /etc/init.d/bind ] ; then if /etc/init.d/bind enabled ; then dns_ast=1 else dns_ast=0 fi else dns_ast=0 fi ;; dnsmasq) if [ -x /usr/sbin/dnsmasq ] && [ -x /etc/init.d/dnsmasq ] ; then if /etc/init.d/dnsmasq enabled ; then dns_ast=1 else dns_ast=0 fi else dns_ast=0 fi ;; htpps-dns-proxy) if [ -x /usr/sbin/https-dns-proxy ] \ && [ -x /etc/init.d/https-dns-proxy ] ; then if /etc/init.d/https-dns-proxy ; then dns_ast=1 else dns_ast=0 fi else dns_ast=0 fi ;; ipset-dns) if [ -x /usr/sbin/ipset-dns ] && [ -x /etc/init.d/ipset-dns ] ; then if /etc/init.d/ipset-dns enabled ; then dns_ast=1 else dns_ast=0 fi else dns_ast=0 fi ;; nsd) if [ -x /usr/sbin/nsd ] && [ -x /etc/init.d/nsd ] ; then if /etc/init.d/nsd enabled ; then dns_ast=1 else dns_ast=0 fi else dns_ast=0 fi ;; unprotected-loop) # Soft brick risk. The server you are looking to connect to may be offline # and cause loop error: procd, sysupgrade, package order, and other issues. dns_ast=1 ;; *) # Unbound has a local forward blocking option, default on, instead of loop # detection. If it is released, then it may be a soft brick risk. dns_ast=0 ;; esac if [ $dns_ast -gt 0 ] ; then UB_B_DNS_ASSIST=1 fi case $zone_type in auth_zone) if [ $UB_B_NTP_BOOT -eq 0 ] && [ -n "$UB_LIST_ZONE_NAMES" ] \ && { [ -n "$url_dir" ] || [ -n "$UB_LIST_ZONE_SERVERS" ] ; } ; then # Note AXFR may have large downloads. If NTP restart is configured, # then this can cause procd to force a process kill. for zone_name in $UB_LIST_ZONE_NAMES ; do if [ "$zone_name" = "." ] ; then zone_sym=. zone_name=root zone_file=root.zone else zone_sym=$zone_name zone_file=$zone_name.zone zone_file=${zone_file//../.} fi { # generate an auth-zone: with switches for prefetch cache echo "auth-zone:" echo " name: $zone_sym" for server in $UB_LIST_ZONE_SERVERS ; do echo " master: $server${port:+@${port}}" done if [ -n "$url_dir" ] ; then echo " url: $url_dir$zone_file" fi echo " fallback-enabled: $fallback" echo " for-downstream: no" echo " for-upstream: yes" echo " zonefile: $zone_file" echo } >> $UB_ZONE_CONF done fi ;; forward_zone) if [ ! -f $UB_TLS_ETC_FILE ] && [ "$tls_upstream" = "yes" ] ; then logger -p 4 -t unbound -s \ "Forward-zone TLS benefits from authentication in package 'ca-bundle'" fi if [ -n "$UB_LIST_ZONE_NAMES" ] && [ -n "$UB_LIST_ZONE_SERVERS" ] ; then for server in $UB_LIST_ZONE_SERVERS ; do if [ "$( valid_subnet_any $server )" = "ok" ] \ || { [ "$( local_subnet $server )" = "ok" ] \ && [ $dns_ast -gt 0 ] ; } ; then case $server in *@[0-9]*|*#[A-Za-z0-9]*) # unique Unbound option for server address servers_ip="$servers_ip $server" ;; *) if [ "$tls_upstream" = "yes" ] ; then servers_ip="$servers_ip $server$tls_suffix" else servers_ip="$servers_ip $server${port:+@${port}}" fi ;; esac else case $server in 127.*|::0*) # soft brick loop back risk see DNS assist above echo "do nothing" >/dev/null ;; *@[0-9]*|*#[A-Za-z0-9]*) # unique Unbound option for server host name servers_host="$servers_host $server" ;; *) if [ "$tls_upstream" = "yes" ] ; then servers_host="$servers_host $server${tls_port:+@${tls_port}}" else servers_host="$servers_host $server${port:+@${port}}" fi ;; esac fi done for zonename in $UB_LIST_ZONE_NAMES ; do { # generate a forward-zone with or without tls echo "forward-zone:" echo " name: $zonename" for server in $servers_host ; do echo " forward-host: $server" done for server in $servers_ip ; do echo " forward-addr: $server" done echo " forward-first: $fallback" echo " forward-tls-upstream: $tls_upstream" echo } >> $UB_ZONE_CONF done fi ;; stub_zone) if [ -n "$UB_LIST_ZONE_NAMES" ] && [ -n "$UB_LIST_ZONE_SERVERS" ] ; then for zonename in $UB_LIST_ZONE_NAMES ; do { # generate a stub-zone: or ensure short cut to authority NS echo "stub-zone:" echo " name: $zonename" for server in $UB_LIST_ZONE_SERVERS ; do echo " stub-addr: $server${port:+@${port}}" done echo " stub-first: $fallback" echo } >> $UB_ZONE_CONF done fi ;; *) { echo " # Special zone $zonename was not enabled or had UCI conflicts." echo } >> $UB_ZONE_CONF ;; esac } ############################################################################## unbound_conf() { local rt_mem rt_conn rt_buff modulestring domain ifsubnet moduleopts { # server: for this whole function echo "# $UB_CORE_CONF generated by UCI $( date -Is )" echo "server:" echo " username: unbound" echo " chroot: $UB_VARDIR" echo " directory: $UB_VARDIR" echo " pidfile: $UB_PIDFILE" } > $UB_CORE_CONF if [ -f "$UB_TLS_ETC_FILE" ] ; then # TLS cert bundle for upstream forwarder and https zone files # This is loaded before drop to root, so pull from /etc/ssl echo " tls-cert-bundle: $UB_TLS_ETC_FILE" >> $UB_CORE_CONF fi if [ -f "$UB_RHINT_FILE" ] ; then # Optional hints if found echo " root-hints: $UB_RHINT_FILE" >> $UB_CORE_CONF fi if [ $UB_B_DNSSEC -gt 0 ] && [ -f "$UB_RKEY_FILE" ] ; then { echo " auto-trust-anchor-file: $UB_RKEY_FILE" echo } >> $UB_CORE_CONF else echo >> $UB_CORE_CONF fi if [ $UB_N_THREADS -gt 1 ] \ && $PROG -V | grep -q "Linked libs:.*libevent" ; then # heavy variant using "threads" may need substantial resources echo " num-threads: 2" >> $UB_CORE_CONF else # light variant with one "process" is much more efficient with light traffic echo " num-threads: 1" >> $UB_CORE_CONF fi { # Limited threading (2) with one shared slab echo " msg-cache-slabs: 1" echo " rrset-cache-slabs: 1" echo " infra-cache-slabs: 1" echo " key-cache-slabs: 1" echo " ratelimit-slabs: 1" echo " ip-ratelimit-slabs: 1" echo # Logging echo " use-syslog: yes" echo " statistics-interval: 0" echo " statistics-cumulative: no" } >> $UB_CORE_CONF if [ $UB_D_VERBOSE -ge 0 ] && [ $UB_D_VERBOSE -le 5 ] ; then echo " verbosity: $UB_D_VERBOSE" >> $UB_CORE_CONF fi if [ $UB_B_EXT_STATS -gt 0 ] ; then { # store more data in memory for unbound-control to report echo " extended-statistics: yes" echo } >> $UB_CORE_CONF else { # store Less echo " extended-statistics: no" echo } >> $UB_CORE_CONF fi if [ $UB_B_IF_AUTO -gt 0 ] ; then echo " interface-automatic: yes" >> $UB_CORE_CONF fi if [ $UB_B_DNS_ASSIST -gt 0 ] ; then echo " do-not-query-localhost: no" >> $UB_CORE_CONF fi { # avoid interference with SPI/NAT on both reserved and common server ports echo " edns-buffer-size: $UB_N_EDNS_SIZE" echo " port: $UB_N_RX_PORT" echo " outgoing-port-permit: 10240-65535" } >> $UB_CORE_CONF case "$UB_D_PROTOCOL" in ip4_only) { echo " do-ip4: yes" echo " do-ip6: no" echo } >> $UB_CORE_CONF ;; ip6_only) { echo " do-ip4: no" echo " do-ip6: yes" echo } >> $UB_CORE_CONF ;; ip6_local) { # answer your local IPv6 network but avoid broken ISP IPv6 echo " do-ip4: yes" echo " do-ip6: yes" echo " prefer-ip4: yes" echo " prefer-ip6: no" echo } >> $UB_CORE_CONF ;; ip6_prefer) { # RFC compliant dual stack echo " do-ip4: yes" echo " do-ip6: yes" echo " prefer-ip4: no" echo " prefer-ip6: yes" echo } >> $UB_CORE_CONF ;; mixed) { echo " do-ip4: yes" echo " do-ip6: yes" echo } >> $UB_CORE_CONF ;; *) if [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "default protocol configuration" fi ;; esac case "$UB_D_RESOURCE" in # Tiny - Unbound's recommended cheap hardware config tiny) rt_mem=1 ; rt_conn=5 ; rt_buff=1 ;; # Small - Half RRCACHE and open ports small) rt_mem=8 ; rt_conn=10 ; rt_buff=2 ;; # Medium - Nearly default but with some added balancintg medium) rt_mem=16 ; rt_conn=20 ; rt_buff=4 ;; # Large - Double medium large) rt_mem=32 ; rt_conn=50 ; rt_buff=4 ;; # Whatever unbound does *) rt_mem=0 ; rt_conn=0 ;; esac if [ $rt_mem -gt 0 ] ; then { # Other harding and options for an embedded router echo " harden-short-bufsize: yes" echo " harden-large-queries: yes" echo " harden-glue: yes" echo " use-caps-for-id: no" echo # Set memory sizing parameters echo " msg-buffer-size: $(($rt_buff*8192))" echo " outgoing-range: $(($rt_conn*32))" echo " num-queries-per-thread: $(($rt_conn*16))" 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 " stream-wait-size: $(($rt_mem*128))k" echo " key-cache-size: $(($rt_mem*128))k" echo " neg-cache-size: $(($rt_mem*32))k" echo " ratelimit-size: $(($rt_mem*32))k" echo " ip-ratelimit-size: $(($rt_mem*32))k" echo " infra-cache-numhosts: $(($rt_mem*256))" echo } >> $UB_CORE_CONF elif [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "default memory configuration" fi # Assembly of module-config: options is tricky; order matters moduleopts="$( /usr/sbin/unbound -V )" modulestring="iterator" case $moduleopts in *with-python*) modulestring="python $modulestring" ;; esac if [ $UB_B_DNSSEC -gt 0 ] ; then if [ $UB_B_NTP_BOOT -gt 0 ] ; then # DNSSEC chicken and egg with getting NTP time echo " val-override-date: -1" >> $UB_CORE_CONF fi { echo " harden-dnssec-stripped: yes" echo " val-clean-additional: yes" echo " ignore-cd-flag: yes" } >> $UB_CORE_CONF modulestring="validator $modulestring" fi case $moduleopts in *enable-subnet*) modulestring="subnetcache $modulestring" ;; esac if [ $UB_B_DNS64 -gt 0 ] ; then echo " dns64-prefix: $UB_IP_DNS64" >> $UB_CORE_CONF modulestring="dns64 $modulestring" fi { # Print final module string echo " module-config: \"$modulestring\"" echo } >> $UB_CORE_CONF case "$UB_D_RECURSION" in passive) { # Some query privacy but "strict" will break some servers if [ $UB_B_QRY_MINST -gt 0 ] && [ "$UB_B_QUERY_MIN" -gt 0 ] ; then echo " qname-minimisation: yes" echo " qname-minimisation-strict: yes" elif [ $UB_B_QUERY_MIN -gt 0 ] ; then echo " qname-minimisation: yes" else echo " qname-minimisation: no" fi # Use DNSSEC to quickly understand NXDOMAIN ranges if [ $UB_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 } >> $UB_CORE_CONF ;; aggressive) { # Some query privacy but "strict" will break some servers if [ $UB_B_QRY_MINST -gt 0 ] && [ $UB_B_QUERY_MIN -gt 0 ] ; then echo " qname-minimisation: yes" echo " qname-minimisation-strict: yes" elif [ $UB_B_QUERY_MIN -gt 0 ] ; then echo " qname-minimisation: yes" else echo " qname-minimisation: no" fi # Use DNSSEC to quickly understand NXDOMAIN ranges if [ $UB_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 } >> $UB_CORE_CONF ;; *) if [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "default recursion configuration" fi ;; esac if [ 10 -lt $UB_N_RATE_LMT ] && [ $UB_N_RATE_LMT -lt 100000 ] ; then { # Protect the server from query floods which is helpful on weaker CPU # Per client rate limit is half the maximum to leave head room open echo " ratelimit: $UB_N_RATE_LMT" echo " ip-ratelimit: $(($UB_N_RATE_LMT/2))" echo } >> $UB_CORE_CONF fi { # Reload records more than 20 hours old # DNSSEC 5 minute bogus cool down before retry # Adaptive infrastructure info kept for 15 minutes echo " cache-min-ttl: $UB_TTL_MIN" echo " cache-max-ttl: 72000" echo " val-bogus-ttl: 300" echo " infra-host-ttl: 900" echo } >> $UB_CORE_CONF if [ $UB_B_HIDE_BIND -gt 0 ] ; then { # Block server id and version DNS TXT records echo " hide-identity: yes" echo " hide-version: yes" echo } >> $UB_CORE_CONF fi if [ $UB_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 } >> $UB_CORE_CONF fi if [ -n "$UB_LIST_NETW_LAN" ] && [ $UB_D_PRIV_BLCK -gt 1 ] ; then { for ifsubnet in $UB_LIST_NETW_LAN ; do case $ifsubnet in *@[1-9][0-9a-f][0-9a-f][0-9a-f]:*:[0-9a-f]*) # Remove global DNS responses with your local network IP6 GLA echo " private-address: ${ifsubnet#*@}" ;; esac done echo } >> $UB_CORE_CONF fi if [ $UB_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 } >> $UB_CORE_CONF fi if [ -n "$UB_LIST_INSECURE" ] ; then { for domain in $UB_LIST_INSECURE ; do # Except and accept domains without (DNSSEC); work around broken domains echo " domain-insecure: $domain" done echo } >> $UB_CORE_CONF fi if [ $UB_B_LOCL_SERV -gt 0 ] && [ -n "$UB_LIST_NETW_LAN" ] ; then { for ifsubnet in $UB_LIST_NETW_LAN ; do # Only respond to queries from subnets which have an interface. # Prevent DNS amplification attacks by not responding to the universe. echo " access-control: ${ifsubnet#*@} allow" done echo " access-control: 127.0.0.0/8 allow" echo " access-control: ::1/128 allow" echo " access-control: fe80::/10 allow" echo } >> $UB_CORE_CONF else { echo " access-control: 0.0.0.0/0 allow" echo " access-control: ::0/0 allow" echo } >> $UB_CORE_CONF fi } ############################################################################## unbound_hostname() { local ifsubnet ifarpa ifaddr ifname iffqdn local ulaprefix hostfqdn name names namerec ptrrec local zonetype=0 echo "# $UB_HOST_CONF generated by UCI $( date -Is )" > $UB_HOST_CONF if [ "$UB_D_DHCP_LINK" = "dnsmasq" ] ; then { echo "# Local zone is handled by dnsmasq" echo } >> $UB_HOST_CONF elif [ -n "$UB_TXT_DOMAIN" ] \ && { [ $UB_D_WAN_FQDN -gt 0 ] || [ $UB_D_LAN_FQDN -gt 0 ] ; } ; then case "$UB_D_DOMAIN_TYPE" in deny|inform_deny|refuse|static) { # type static means only this router has your domain echo " domain-insecure: $UB_TXT_DOMAIN" echo " private-domain: $UB_TXT_DOMAIN" echo " local-zone: $UB_TXT_DOMAIN $UB_D_DOMAIN_TYPE" echo " local-data: \"$UB_TXT_DOMAIN. $UB_XSOA\"" echo " local-data: \"$UB_TXT_DOMAIN. $UB_XNS\"" echo " local-data: '$UB_TXT_DOMAIN. $UB_XTXT'" echo if [ "$UB_TXT_DOMAIN" != "local" ] ; then # avoid involvement in RFC6762, unless it is the local zone name echo " local-zone: local always_nxdomain" echo fi } >> $UB_HOST_CONF zonetype=2 ;; inform|transparent|typetransparent) { # transparent will permit forward-zone: or stub-zone: clauses echo " private-domain: $UB_TXT_DOMAIN" echo " local-zone: $UB_TXT_DOMAIN $UB_D_DOMAIN_TYPE" echo } >> $UB_HOST_CONF zonetype=1 ;; esac { # Hostname as TLD works, but not transparent through recursion (singular) echo " domain-insecure: $UB_TXT_HOSTNAME" echo " private-domain: $UB_TXT_HOSTNAME" echo " local-zone: $UB_TXT_HOSTNAME static" echo " local-data: \"$UB_TXT_HOSTNAME. $UB_XSOA\"" echo " local-data: \"$UB_TXT_HOSTNAME. $UB_XNS\"" echo " local-data: '$UB_TXT_HOSTNAME. $UB_XTXT'" echo } >> $UB_HOST_CONF if [ -n "$UB_LIST_NETW_WAN" ] ; then for ifsubnet in $UB_LIST_NETW_WAN ; do ifaddr=${ifsubnet#*@} ifaddr=${ifaddr%/*} ifarpa=$( host_ptr_any "$ifaddr" ) if [ -n "$ifarpa" ] ; then if [ $UB_D_WAN_FQDN -gt 0 ] ; then { # Create a static zone for WAN host record only (singular) echo " domain-insecure: $ifarpa" echo " private-address: $ifaddr" echo " local-zone: $ifarpa static" echo " local-data: \"$ifarpa. $UB_XSOA\"" echo " local-data: \"$ifarpa. $UB_XNS\"" echo " local-data: '$ifarpa. $UB_MTXT'" echo } >> $UB_HOST_CONF elif [ $zonetype -gt 0 ] ; then { echo " local-zone: $ifarpa transparent" echo } >> $UB_HOST_CONF fi fi done fi if [ -n "$UB_LIST_NETW_LAN" ] ; then for ifsubnet in $UB_LIST_NETW_LAN ; do ifarpa=$( domain_ptr_any "${ifsubnet#*@}" ) if [ -n "$ifarpa" ] ; then if [ $zonetype -eq 2 ] ; then { # Do NOT forward queries with your ip6.arpa or in-addr.arpa echo " domain-insecure: $ifarpa" echo " local-zone: $ifarpa static" echo " local-data: \"$ifarpa. $UB_XSOA\"" echo " local-data: \"$ifarpa. $UB_XNS\"" echo " local-data: '$ifarpa. $UB_XTXT'" echo } >> $UB_HOST_CONF elif [ $zonetype -eq 1 ] && [ $UB_D_PRIV_BLCK -eq 0 ] ; then { echo " local-zone: $ifarpa transparent" echo } >> $UB_HOST_CONF fi fi done fi ulaprefix=$( uci_get network.@globals[0].ula_prefix ) ulaprefix=${ulaprefix%%:/*} hostfqdn="$UB_TXT_HOSTNAME.$UB_TXT_DOMAIN" if [ -z "$ulaprefix" ] ; then # Nonsense so this option isn't globbed below ulaprefix="fdno:such:addr::" fi if [ "$UB_LIST_NETW_LAN" ] && [ $UB_D_LAN_FQDN -gt 0 ] ; then for ifsubnet in $UB_LIST_NETW_LAN ; do ifaddr=${ifsubnet#*@} ifaddr=${ifaddr%/*} ifname=${ifsubnet%@*} iffqdn="$ifname.$hostfqdn" if [ $UB_D_LAN_FQDN -eq 4 ] ; then names="$iffqdn $hostfqdn $UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $iffqdn\"" echo "$ptrrec" >> $UB_HOST_CONF elif [ $UB_D_LAN_FQDN -eq 3 ] ; then names="$hostfqdn $UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $hostfqdn\"" echo "$ptrrec" >> $UB_HOST_CONF else names="$UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $UB_TXT_HOSTNAME\"" echo "$ptrrec" >> $UB_HOST_CONF fi for name in $names ; do case $ifaddr in "${ulaprefix}"*) # IP6 ULA only is assigned for OPTION 1 namerec=" local-data: \"$name. 300 IN AAAA $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF ;; [1-9]*.*[0-9]) namerec=" local-data: \"$name. 300 IN A $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF ;; *) if [ $UB_D_LAN_FQDN -gt 1 ] ; then # IP6 GLA is assigned for higher options namerec=" local-data: \"$name. 300 IN AAAA $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF fi ;; esac done echo >> $UB_HOST_CONF done fi if [ -n "$UB_LIST_NETW_WAN" ] && [ $UB_D_WAN_FQDN -gt 0 ] ; then for ifsubnet in $UB_LIST_NETW_WAN ; do ifaddr=${ifsubnet#*@} ifaddr=${ifaddr%/*} ifname=${ifsubnet%@*} iffqdn="$ifname.$hostfqdn" if [ $UB_D_WAN_FQDN -eq 4 ] ; then names="$iffqdn $hostfqdn $UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $iffqdn\"" echo "$ptrrec" >> $UB_HOST_CONF elif [ $UB_D_WAN_FQDN -eq 3 ] ; then names="$hostfqdn $UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $hostfqdn\"" echo "$ptrrec" >> $UB_HOST_CONF else names="$UB_TXT_HOSTNAME" ptrrec=" local-data-ptr: \"$ifaddr 300 $UB_TXT_HOSTNAME\"" echo "$ptrrec" >> $UB_HOST_CONF fi for name in $names ; do case $ifaddr in "${ulaprefix}"*) # IP6 ULA only is assigned for OPTION 1 namerec=" local-data: \"$name. 300 IN AAAA $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF ;; [1-9]*.*[0-9]) namerec=" local-data: \"$name. 300 IN A $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF ;; *) if [ $UB_D_WAN_FQDN -gt 1 ] ; then # IP6 GLA is assigned for higher options namerec=" local-data: \"$name. 300 IN AAAA $ifaddr\"" echo "$namerec" >> $UB_HOST_CONF fi ;; esac done echo >> $UB_HOST_CONF done fi fi # end if uci valid } ############################################################################## unbound_uci() { local cfg="$1" local hostnm hostnm=$( uci_get system.@system[0].hostname | awk '{print tolower($0)}' ) UB_TXT_HOSTNAME=${hostnm:-thisrouter} config_get_bool UB_B_SLAAC6_MAC "$cfg" dhcp4_slaac6 0 config_get_bool UB_B_DNS64 "$cfg" dns64 0 config_get_bool UB_B_EXT_STATS "$cfg" extended_stats 0 config_get_bool UB_B_HIDE_BIND "$cfg" hide_binddata 1 config_get_bool UB_B_LOCL_SERV "$cfg" localservice 1 config_get_bool UB_B_MAN_CONF "$cfg" manual_conf 0 config_get_bool UB_B_QUERY_MIN "$cfg" query_minimize 0 config_get_bool UB_B_QRY_MINST "$cfg" query_min_strict 0 config_get_bool UB_B_AUTH_ROOT "$cfg" prefetch_root 0 config_get_bool UB_B_LOCL_BLCK "$cfg" rebind_localhost 0 config_get_bool UB_B_DNSSEC "$cfg" validator 0 config_get_bool UB_B_NTP_BOOT "$cfg" validator_ntp 1 config_get_bool UB_B_IF_AUTO "$cfg" interface_auto 1 config_get UB_IP_DNS64 "$cfg" dns64_prefix "64:ff9b::/96" config_get UB_N_EDNS_SIZE "$cfg" edns_size 1232 config_get UB_N_RX_PORT "$cfg" listen_port 53 config_get UB_N_ROOT_AGE "$cfg" root_age 9 config_get UB_N_THREADS "$cfg" num_threads 1 config_get UB_N_RATE_LMT "$cfg" rate_limit 0 config_get UB_D_CONTROL "$cfg" unbound_control 0 config_get UB_D_DOMAIN_TYPE "$cfg" domain_type static config_get UB_D_DHCP_LINK "$cfg" dhcp_link none config_get UB_D_EXTRA_DNS "$cfg" add_extra_dns 0 config_get UB_D_LAN_FQDN "$cfg" add_local_fqdn 0 config_get UB_D_PRIV_BLCK "$cfg" rebind_protection 1 config_get UB_D_PROTOCOL "$cfg" protocol mixed config_get UB_D_RECURSION "$cfg" recursion passive config_get UB_D_RESOURCE "$cfg" resource small config_get UB_D_VERBOSE "$cfg" verbosity 1 config_get UB_D_WAN_FQDN "$cfg" add_wan_fqdn 0 config_get UB_TTL_MIN "$cfg" ttl_min 120 config_get UB_TXT_DOMAIN "$cfg" domain lan config_list_foreach "$cfg" domain_insecure bundle_domain_insecure config_list_foreach "$cfg" iface_lan bundle_lan_networks config_list_foreach "$cfg" iface_wan bundle_wan_networks if [ "$UB_D_DHCP_LINK" = "none" ] ; then config_get_bool UB_B_DNSMASQ "$cfg" dnsmasq_link_dns 0 if [ $UB_B_DNSMASQ -gt 0 ] ; then UB_D_DHCP_LINK=dnsmasq if [ $UB_B_READY -eq 0 ] ; then logger -t unbound -s "Please use 'dhcp_link' selector instead" fi fi fi if [ "$UB_D_DHCP_LINK" = "dnsmasq" ] ; then if [ ! -x /usr/sbin/dnsmasq ] || [ ! -x /etc/init.d/dnsmasq ] ; then UB_D_DHCP_LINK=none else /etc/init.d/dnsmasq enabled || UB_D_DHCP_LINK=none fi if [ $UB_B_READY -eq 0 ] && [ "$UB_D_DHCP_LINK" = "none" ] ; then logger -t unbound -s "cannot forward to dnsmasq" fi fi if [ "$UB_D_DHCP_LINK" = "odhcpd" ] ; then if [ ! -x /usr/sbin/odhcpd ] || [ ! -x /etc/init.d/odhcpd ] ; then UB_D_DHCP_LINK=none else /etc/init.d/odhcpd enabled || UB_D_DHCP_LINK=none fi if [ $UB_B_READY -eq 0 ] && [ "$UB_D_DHCP_LINK" = "none" ] ; then logger -t unbound -s "cannot receive records from odhcpd" fi fi if [ $UB_N_EDNS_SIZE -lt 512 ] || [ 4096 -lt $UB_N_EDNS_SIZE ] ; then logger -t unbound -s "edns_size exceeds range, using default" UB_N_EDNS_SIZE=1232 fi if [ $UB_N_RX_PORT -ne 53 ] \ && { [ $UB_N_RX_PORT -lt 1024 ] || [ 10240 -lt $UB_N_RX_PORT ] ; } ; then logger -t unbound -s "privileged port or in 5 digits, using default" UB_N_RX_PORT=53 fi if [ $UB_TTL_MIN -gt 1800 ] ; then logger -t unbound -s "ttl_min could have had awful side effects, using 300" UB_TTL_MIN=300 fi } ############################################################################## unbound_include() { local adb_enabled local adb_files=$( ls $UB_VARDIR/adb_list.* 2>/dev/null ) echo "# $UB_TOTAL_CONF generated by UCI $( date -Is )" > $UB_TOTAL_CONF if [ -f "$UB_CORE_CONF" ] ; then # Yes this all looks busy, but it is in TMPFS. Working on separate files # and piecing together is easier. UCI order is less constrained. cat $UB_CORE_CONF >> $UB_TOTAL_CONF rm $UB_CORE_CONF fi if [ -f "$UB_HOST_CONF" ] ; then # UCI definitions of local host or local subnet cat $UB_HOST_CONF >> $UB_TOTAL_CONF rm $UB_HOST_CONF fi if [ -f $UB_SRVMASQ_CONF ] ; then # UCI found link to dnsmasq cat $UB_SRVMASQ_CONF >> $UB_TOTAL_CONF rm $UB_SRVMASQ_CONF fi if [ -f "$UB_DHCP_CONF" ] ; then { # Seed DHCP records because dhcp scripts trigger externally # Incremental Unbound restarts may drop unbound-control records echo "include: $UB_DHCP_CONF" echo } >> $UB_TOTAL_CONF fi if [ -z "$adb_files" ] || [ ! -x /usr/bin/adblock.sh ] \ || [ ! -x /etc/init.d/adblock ] ; then adb_enabled=0 elif /etc/init.d/adblock enabled ; then adb_enabled=1 { # Pull in your selected openwrt/pacakges/net/adblock generated lists echo "include: $UB_VARDIR/adb_list.*" echo } >> $UB_TOTAL_CONF else adb_enabled=0 fi if [ -f $UB_SRV_CONF ] ; then { # Pull your own "server:" options here echo "include: $UB_SRV_CONF" echo } >> $UB_TOTAL_CONF fi if [ -f "$UB_ZONE_CONF" ] ; then # UCI defined forward, stub, and auth zones cat $UB_ZONE_CONF >> $UB_TOTAL_CONF rm $UB_ZONE_CONF fi if [ -f "$UB_CTRL_CONF" ] ; then # UCI defined control application connection cat $UB_CTRL_CONF >> $UB_TOTAL_CONF rm $UB_CTRL_CONF fi if [ -f "$UB_EXTMASQ_CONF" ] ; then # UCI found link to dnsmasq cat $UB_EXTMASQ_CONF >> $UB_TOTAL_CONF rm $UB_EXTMASQ_CONF fi if [ -f "$UB_EXT_CONF" ] ; then { # Pull your own extend feature clauses here echo "include: $UB_EXT_CONF" echo } >> $UB_TOTAL_CONF fi } ############################################################################## resolv_setup() { if [ "$UB_N_RX_PORT" != "53" ] ; then # unbound is not the default on target resolver echo "do nothing" >/dev/null elif [ -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 # is already listening on port 53. Let dnsmasq manage resolve.conf. # This also works to prevent clobbering while changing UCI. echo "do nothing" >/dev/null else # unbound listens on 127.0.0.1#53 so set resolver file to local. rm -f $UB_RESOLV_CONF { echo "# $UB_RESOLV_CONF generated by Unbound UCI $( date -Is )" echo "nameserver 127.0.0.1" echo "nameserver ::1" echo "search $UB_TXT_DOMAIN." } > $UB_RESOLV_CONF fi } ############################################################################## unbound_start() { # get interface subnets together config_load network config_foreach bundle_all_networks interface # read Unbound UCI but pick through it later config_load unbound config_foreach unbound_uci unbound unbound_mkdir if [ $UB_B_MAN_CONF -eq 0 ] ; then # iterate zones before we load other UCI # forward-zone: auth-zone: and stub-zone: config_foreach unbound_zone zone # associate potential DNS RR with interfaces config_load dhcp config_foreach bundle_dhcp_networks dhcp # server: unbound_conf unbound_hostname # control: unbound_control # dnsmasq dnsmasq_link # merge unbound_include fi resolv_setup } ##############################################################################