]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/initscripts/system/unbound
unbound: skip empty domains at local-data import
[ipfire-2.x.git] / src / initscripts / system / unbound
index a4a356a23075c10315f438392f25a81d3e0b9c76..acbf6f5b5216f761a9550cbcd0350ba1a5ea7149 100644 (file)
@@ -7,28 +7,12 @@
 . /etc/sysconfig/rc
 . ${rc_functions}
 
-TEST_DOMAIN="ipfire.org"
-
-# This domain will never validate
-TEST_DOMAIN_FAIL="dnssec-failed.org"
-
-INSECURE_ZONES=
-USE_FORWARDERS=1
-ENABLE_SAFE_SEARCH=off
-FORCE_TCP=off
-FORCE_TLS=off
-
 # Cache any local zones for 60 seconds
 LOCAL_TTL=60
 
-# Load optional configuration
-[ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
-
-DIG_ARGS=()
-
-if [ "${FORCE_TCP}" = "on" ]; then
-       DIG_ARGS+=( "+tcp" )
-fi
+# Load configuration
+eval $(/usr/local/bin/readhash /var/ipfire/dns/settings)
+eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
 
 ip_address_revptr() {
        local addr=${1}
@@ -40,26 +24,27 @@ ip_address_revptr() {
 }
 
 read_name_servers() {
-       local i
-       for i in 1 2; do
-               echo "$(</var/ipfire/red/dns${i})"
-       done 2>/dev/null | xargs echo
-}
-
-check_red_has_carrier_and_ip() {
-       # Interface configured ?
-       [ ! -e "/var/ipfire/red/iface" ] && return 0;
-
-       # Interface present ?
-       [ ! -e "/sys/class/net/$(</var/ipfire/red/iface)" ] && return 0;
-
-       # has carrier ?
-       [ ! "$(</sys/class/net/$(</var/ipfire/red/iface)/carrier)" = "1" ] && return 0;
+       # Read name servers from ISP
+       if [ "${USE_ISP_NAMESERVERS}" = "on" -a "${PROTO}" != "TLS" ]; then
+               local i
+               for i in 1 2; do
+                       echo "$(</var/run/dns${i})"
+               done 2>/dev/null
+       fi
 
-       # has ip ?
-       [ "$(ip address show dev $(</var/ipfire/red/iface) | grep "inet")" = "" ] && return 0;
+       # Read configured name servers
+       local id address tls_hostname enabled remark
+       while IFS="," read -r id address tls_hostname enabled remark; do
+               [ "${enabled}" != "enabled" ] && continue
 
-       return 1;
+               if [ "${PROTO}" = "TLS" ]; then
+                       if [ -n "${tls_hostname}" ]; then
+                               echo "${address}@853#${tls_hostname}"
+                       fi
+               else
+                       echo "${address}"
+               fi
+       done < /var/ipfire/dns/servers
 }
 
 config_header() {
@@ -68,139 +53,78 @@ config_header() {
        echo
 }
 
-update_forwarders() {
-       check_red_has_carrier_and_ip
-       if [ "${USE_FORWARDERS}" = "1" -a "${?}" = "1" ]; then
-               local forwarders
-               local broken_forwarders
-
-               local ns
-               for ns in $(read_name_servers); do
-                       test_name_server ${ns} &>/dev/null
-                       case "$?" in
-                               # Only use DNSSEC-validating or DNSSEC-aware name servers
-                               0|2)
-                                       forwarders="${forwarders} ${ns}"
-                                       ;;
-                               *)
-                                       broken_forwarders="${broken_forwarders} ${ns}"
-                                       ;;
-                       esac
-               done
-
-               # Show warning for any broken upstream name servers
-               if [ -n "${broken_forwarders}" ]; then
-                       boot_mesg "Ignoring broken upstream name server(s): ${broken_forwarders:1}" ${WARNING}
-                       echo_warning
-               fi
-
-               if [ -n "${forwarders}" ]; then
-                       boot_mesg "Configuring upstream name server(s): ${forwarders:1}" ${INFO}
-                       echo_ok
-
-                       # Make sure DNSSEC is activated
-                       enable_dnssec
-
-                       echo "${forwarders}" > /var/ipfire/red/dns
-                       unbound-control -q forward ${forwarders}
-                       return 0
-
-               # In case we have found no working forwarders
-               else
-                       # Test if the recursor mode is available
-                       if can_resolve_root; then
-                               # Make sure DNSSEC is activated
-                               enable_dnssec
-
-                               boot_mesg "Falling back to recursor mode" ${WARNING}
-                               echo_warning
-
-                       # If not, we set DNSSEC in permissive mode and allow using all recursors
-                       elif [ -n "${broken_forwarders}" ]; then
-                               disable_dnssec
-
-                               boot_mesg "DNSSEC has been set to permissive mode" ${FAILURE}
-                               echo_failure
+write_hosts_conf() {
+       (
+               config_header
 
-                               echo "${broken_forwarders}" > /var/ipfire/red/dns
-                               unbound-control -q forward ${broken_forwarders}
-                               return 0
-                       fi
+               # Make own hostname resolveable
+               # 1.1.1.1 is reserved for unused green, skip this
+               if [ -n "${GREEN_ADDRESS}" -a "${GREEN_ADDRESS}" != "1.1.1.1" ]; then
+                       echo "local-data: \"${HOSTNAME} ${LOCAL_TTL} IN A ${GREEN_ADDRESS}\""
                fi
-       fi
-
-       # If forwarders cannot be used we run in recursor mode
-       echo "local recursor" > /var/ipfire/red/dns
-       unbound-control -q forward off
-}
 
-remove_forwarders() {
-       enable_dnssec
-       echo "local recursor" > /var/ipfire/red/dns
-       unbound-control -q forward off
+               local address
+               for address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do
+                       [ -n "${address}" ] || continue
+                       [ "${address}" = "1.1.1.1" ] && continue
 
-}
-
-own_hostname() {
-       local hostname=$(hostname -f)
-       # 1.1.1.1 is reserved for unused green, skip this
-       if [ -n "${GREEN_ADDRESS}" -a "${GREEN_ADDRESS}" != "1.1.1.1" ]; then
-               unbound-control -q local_data "${hostname} ${LOCAL_TTL} IN A ${GREEN_ADDRESS}"
-       fi
+                       address=$(ip_address_revptr ${address})
+                       echo "local-data: \"${address} ${LOCAL_TTL} IN PTR ${HOSTNAME}\""
+               done
 
-       local address
-       for address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do
-               [ -n "${address}" ] || continue
-               [ "${address}" = "1.1.1.1" ] && continue
+               local enabled address hostname domainname generateptr
 
-               address=$(ip_address_revptr ${address})
-               unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${hostname}"
-       done
-}
+               # Find all unique domain names
+               while IFS="," read -r enabled address hostname domainname generateptr; do
+                       [ "${enabled}" = "on" ] || continue
 
-update_hosts() {
-       local enabled address hostname domainname generateptr
+                       # Skip empty domainnames
+                       [ "${domainname}" = "" ] && continue
 
-       while IFS="," read -r enabled address hostname domainname generateptr; do
-               [ "${enabled}" = "on" ] || continue
+                       echo "local-zone: ${domainname} typetransparent"
+               done < /var/ipfire/main/hosts | sort -u
 
-               # Build FQDN
-               local fqdn="${hostname}.${domainname}"
+               # Add all hosts
+               while IFS="," read -r enabled address hostname domainname generateptr; do
+                       [ "${enabled}" = "on" ] || continue
 
-               unbound-control -q local_data "${fqdn} ${LOCAL_TTL} IN A ${address}"
+                       # Build FQDN
+                       local fqdn="${hostname}.${domainname}"
+                       echo "local-data: \"${fqdn} ${LOCAL_TTL} IN A ${address}\""
 
-               # Skip reverse resolution if the address equals the GREEN address
-               [ "${address}" = "${GREEN_ADDRESS}" ] && continue
+                       # Skip reverse resolution if the address equals the GREEN address
+                       [ "${address}" = "${GREEN_ADDRESS}" ] && continue
 
-               # Skip reverse resolution if user requested not to do so
-               [ "${generateptr}" = "off" ] && continue
+                       # Skip reverse resolution if user requested not to do so
+                       [ "${generateptr}" = "off" ] && continue
 
-               # Add RDNS
-               address=$(ip_address_revptr ${address})
-               unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${fqdn}"
-       done < /var/ipfire/main/hosts
+                       # Add RDNS
+                       address=$(ip_address_revptr ${address})
+                       echo "local-data: \"${address} ${LOCAL_TTL} IN PTR ${fqdn}\""
+               done < /var/ipfire/main/hosts
+       ) > /etc/unbound/hosts.conf
 }
 
 write_forward_conf() {
        (
                config_header
 
-               # Force using TLS for upstream servers only
-               if [ "${FORCE_TLS}" = "on" ]; then
-                       echo "# Force using TLS for upstream servers only"
+               # Enable strict QNAME minimisation
+               if [ "${QNAME_MIN}" = "strict" ]; then
                        echo "server:"
-                       echo "  tls-upstream: yes"
+                       echo "  qname-minimisation-strict: yes"
                        echo
+               fi
 
                # Force using TCP for upstream servers only
-               elif [ "${FORCE_TCP}" = "on" ]; then
+               if [ "${PROTO}" = "TCP" ]; then
                        echo "# Force using TCP for upstream servers only"
                        echo "server:"
                        echo "  tcp-upstream: yes"
                        echo
                fi
 
-               local insecure_zones="${INSECURE_ZONES}"
+               local insecure_zones=""
 
                local enabled zone server servers remark disable_dnssec rest
                while IFS="," read -r enabled zone servers remark disable_dnssec rest; do
@@ -248,21 +172,36 @@ write_forward_conf() {
                                echo "  domain-insecure: ${zone}"
                        done
                fi
+
+               # Read name servers.
+               nameservers=$(read_name_servers)
+
+               # Only write forward zones if any nameservers are configured.
+               #
+               # Otherwise fall-back into recursor mode.
+               if [ -n "${nameservers}" ]; then
+
+                       echo "forward-zone:"
+                       echo "  name: \".\""
+
+                       # Force using TLS only
+                       if [ "${PROTO}" = "TLS" ]; then
+                               echo "  forward-tls-upstream: yes"
+                       fi
+
+                       # Add upstream name servers
+                       local ns
+                       for ns in ${nameservers}; do
+                               echo "  forward-addr: ${ns}"
+                       done
+               fi
+
        ) > /etc/unbound/forward.conf
 }
 
 write_tuning_conf() {
        # https://www.unbound.net/documentation/howto_optimise.html
 
-       # Determine number of online processors
-       local processors=$(getconf _NPROCESSORS_ONLN)
-
-       # Determine number of slabs
-       local slabs=1
-       while [ ${slabs} -lt ${processors} ]; do
-               slabs=$(( ${slabs} * 2 ))
-       done
-
        # Determine amount of system memory
        local mem=$(get_memory_amount)
 
@@ -297,16 +236,6 @@ write_tuning_conf() {
        (
                config_header
 
-               # We run one thread per processor
-               echo "num-threads: ${processors}"
-               echo "so-reuseport: yes"
-
-               # Adjust number of slabs
-               echo "infra-cache-slabs: ${slabs}"
-               echo "key-cache-slabs: ${slabs}"
-               echo "msg-cache-slabs: ${slabs}"
-               echo "rrset-cache-slabs: ${slabs}"
-
                # Slice up the cache
                echo "rrset-cache-size: $(( ${mem} / 2 ))m"
                echo "msg-cache-size: $(( ${mem} / 4 ))m"
@@ -336,170 +265,31 @@ get_memory_amount() {
        done < /proc/meminfo
 }
 
-test_name_server() {
-       local ns=${1}
-       local args
-
-       # Return codes:
-       # 0     DNSSEC validating
-       # 1     Error: unreachable, etc.
-       # 2     DNSSEC aware
-       # 3     NOT DNSSEC-aware
-
-       # Exit when the server is not reachable
-       ns_is_online ${ns} || return 1
-
-       local errors
-       for rr in DNSKEY DS RRSIG; do
-               if ! ns_forwards_${rr} ${ns} ${args}; then
-                       errors="${errors} ${rr}"
-               fi
-       done
-
-       if [ -n "${errors}" ]; then
-               echo >&2 "Unable to retrieve the following resource records from ${ns}: ${errors:1}"
-               return 3
-       fi
-
-       if ns_is_validating ${ns} ${args}; then
-               # Return 0 if validating
-               return 0
-       else
-               # Is DNSSEC-aware
-               return 2
-       fi
-}
-
-# Sends an A query to the nameserver w/o DNSSEC
-ns_is_online() {
-       local ns=${1}
-       shift
-
-       dig "${DIG_ARGS[@]}" @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
-}
-
-# Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
-ns_is_validating() {
-       local ns=${1}
-       shift
-
-       if ! dig "${DIG_ARGS[@]}" @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
-               return 1
-       else
-               # Determine if NS replies with "ad" data flag if DNSSEC enabled
-               dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
-       fi
-}
-
-# Checks if we can retrieve the DNSKEY for this domain.
-# dig will print the SOA if nothing was found
-ns_forwards_DNSKEY() {
-       local ns=${1}
-       shift
-
-       dig "${DIG_ARGS[@]}" @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
-}
-
-ns_forwards_DS() {
-       local ns=${1}
-       shift
-
-       dig "${DIG_ARGS[@]}" @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
-}
-
-ns_forwards_RRSIG() {
-       local ns=${1}
-       shift
-
-       dig "${DIG_ARGS[@]}" @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
-}
-
-ns_supports_tcp() {
-       local ns=${1}
-       shift
-
-       # If TCP is forced we know by now if the server responds to it
-       if [ "${FORCE_TCP}" = "on" ]; then
+fix_time_if_dns_fails() {
+       # If DNS is working, everything is fine
+       if resolve "ping.ipfire.org" &>/dev/null; then
                return 0
        fi
 
-       dig "${DIG_ARGS[@]}" @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
-}
-
-get_root_nameservers() {
-       while read -r hostname ttl record address; do
-               # Searching for A records
-               [ "${record}" = "A" ] || continue
-
-               echo "${address}"
-       done < /etc/unbound/root.hints
-}
-
-can_resolve_root() {
-       local ns
-       for ns in $(get_root_nameservers); do
-               if dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA . $@ >/dev/null; then
-                       return 0
-               fi
-       done
-
-       # none of the servers was reachable
-       return 1
-}
-
-enable_dnssec() {
-       local status=$(unbound-control get_option val-permissive-mode)
-
-       # Log DNSSEC status
-       echo "on" > /var/ipfire/red/dnssec-status
-
-       # Don't do anything if DNSSEC is already activated
-       [ "${status}" = "no" ] && return 0
-
-       # Activate DNSSEC and flush cache with any stale and unvalidated data
-       unbound-control -q set_option val-permissive-mode: no
-       unbound-control -q flush_zone .
-}
-
-disable_dnssec() {
-       # Log DNSSEC status
-       echo "off" > /var/ipfire/red/dnssec-status
-
-       unbound-control -q set_option val-permissive-mode: yes
-}
-
-fix_time_if_dns_fail() {
-       # If DNS still not work try to init ntp with
-       # hardcoded ntp.ipfire.org (81.3.27.46)
-       check_red_has_carrier_and_ip
-       if [ -e "/var/ipfire/red/iface" -a "${?}" = "1" ]; then
-               host 0.ipfire.pool.ntp.org > /dev/null 2>&1
-               if [ "${?}" != "0" ]; then
-                       boot_mesg "DNS still not functioning... Trying to sync time with ntp.ipfire.org (81.3.27.46)..."
-                       loadproc /usr/local/bin/settime 81.3.27.46
-               fi
-       fi
+       # Try to sync time with a known time server
+       boot_mesg "DNS not functioning... Trying to sync time with ntp.ipfire.org (81.3.27.46)..."
+       loadproc /usr/local/bin/settime 81.3.27.46
 }
 
 resolve() {
        local hostname="${1}"
-
-       local found=0
-       local ns
-       for ns in $(read_name_servers); do
-               local answer
-               for answer in $(dig "${DIG_ARGS[@]}" +short "@${ns}" A "${hostname}"); do
-                       found=1
-
-                       # Filter out non-IP addresses
-                       if [[ ! "${answer}" =~ \.$ ]]; then
-                               echo "${answer}"
-                       fi
-               done
-
-               # End loop when we have got something
-               [ ${found} -eq 1 ] && break
+       local found=1
+
+       local answer
+       for answer in $(dig +short A "${hostname}"); do
+               # Filter out non-IP addresses
+               if [[ ! "${answer}" =~ \.$ ]]; then
+                       found=0
+                       echo "${answer}"
+               fi
        done
+
+       return ${found}
 }
 
 # Sets up Safe Search for various search engines
@@ -734,7 +524,7 @@ update_safe_search() {
        for domain in ${google_tlds[@]}; do
                unbound-control local_zone "${domain}" transparent >/dev/null
                for address in ${addresses}; do
-                       unbound-control local_data: "www.${domain} ${LOCAL_TTL} IN A ${address}"
+                       unbound-control local_data "www.${domain} ${LOCAL_TTL} IN A ${address}"
                done >/dev/null
        done
 
@@ -763,30 +553,18 @@ case "$1" in
                        exit 0
                fi
 
-               eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
-
                # Update configuration files
                write_tuning_conf
+               write_hosts_conf
                write_forward_conf
 
                boot_mesg "Starting Unbound DNS Proxy..."
                loadproc /usr/sbin/unbound || exit $?
 
-               # Make own hostname resolveable
-               own_hostname
-
-               # Update any known forwarding name servers
-               update_forwarders
-
                # Install Safe Search rules when the system is already online
                if [ -e "/var/ipfire/red/active" ]; then
                        update_safe_search
                fi
-
-               # Update hosts
-               update_hosts
-
-               fix_time_if_dns_fail
                ;;
 
        stop)
@@ -799,79 +577,38 @@ case "$1" in
                sleep 1
                $0 start
                ;;
+       reload|update-forwarders)
+               # Update configuration files
+               write_forward_conf
+               write_hosts_conf
 
-       status)
-               statusproc /usr/sbin/unbound
-               ;;
-
-       update-forwarders)
-               # Do not try updating forwarders when unbound is not running
-               if ! pgrep unbound &>/dev/null; then
-                       exit 0
-               fi
-
-               update_forwarders
-
-               unbound-control flush_negative > /dev/null
-               unbound-control flush_bogus > /dev/null
+               # Call unbound-control and perform the reload
+               /usr/sbin/unbound-control -q reload
 
-               fix_time_if_dns_fail
-               ;;
+               # Dummy Resolve to wait for unbound
+               resolve "ping.ipfire.org" &>/dev/null
 
-       remove-forwarders)
-               # Do not try updating forwarders when unbound is not running
-               if ! pgrep unbound &>/dev/null; then
-                       exit 0
+               if [ "$1" = "update-forwarders" ]; then
+                       # Make sure DNS works at this point
+                       fix_time_if_dns_fails
                fi
 
-               remove_forwarders
-
-               unbound-control flush_negative > /dev/null
-               unbound-control flush_bogus > /dev/null
-               ;;
-
-
-       test-name-server)
-               ns=${2}
-
-               test_name_server ${ns}
-               ret=${?}
-
-               case "${ret}" in
-                       0)
-                               echo "${ns} is validating"
-                               ;;
-                       2)
-                               echo "${ns} is DNSSEC-aware"
-                               ;;
-                       3)
-                               echo "${ns} is NOT DNSSEC-aware"
-                               ;;
-                       *)
-                               echo "Test failed for an unknown reason"
-                               exit ${ret}
-                               ;;
-               esac
-
-               if ns_supports_tcp ${ns}; then
-                       echo "${ns} supports TCP fallback"
-               else
-                       echo "${ns} does not support TCP fallback"
+               # Update Safe Search rules if the system is online.
+               if [ -e "/var/ipfire/red/active" ]; then
+                       update_safe_search
                fi
-
-               exit ${ret}
                ;;
 
-       resolve)
-               resolve "${2}"
+       status)
+               statusproc /usr/sbin/unbound
                ;;
 
-       update-safe-search)
-               update_safe_search
+       resolve)
+               resolve "${2}" || exit $?
                ;;
 
        *)
-               echo "Usage: $0 {start|stop|restart|status|update-forwarders|remove-forwarders|test-name-server|resolve|update-safe-search}"
+               echo "Usage: $0 {start|stop|restart|reload|status|resolve|update-forwarders}"
                exit 1
                ;;
 esac