#!/bin/sh # Begin $rc_base/init.d/unbound # Description : Unbound DNS resolver boot script for IPfire # Author : Marcel Lorenz . /etc/sysconfig/rc . ${rc_functions} TEST_DOMAIN="ipfire.org" # This domain will never validate TEST_DOMAIN_FAIL="dnssec-failed.org" USE_FORWARDERS=1 # Cache any local zones for 60 seconds LOCAL_TTL=60 # Load optional configuration [ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound function cidr() { local cidr nbits IFS; IFS=. read -r i1 i2 i3 i4 <<< ${1} IFS=. read -r m1 m2 m3 m4 <<< ${2} cidr=$(printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))") nbits=0 IFS=. for dec in $2 ; do case $dec in 255) let nbits+=8;; 254) let nbits+=7;; 252) let nbits+=6;; 248) let nbits+=5;; 240) let nbits+=4;; 224) let nbits+=3;; 192) let nbits+=2;; 128) let nbits+=1;; 0);; *) echo "Error: $dec is not recognised"; exit 1 esac done echo "${cidr}/${nbits}" } read_name_servers() { local i for i in 1 2; do echo "$(/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 "${broken_forwarders}" -a -z "${forwarders}" ]; then boot_mesg "Falling back to recursor mode" ${WARNING} echo_warning elif [ -n "${forwarders}" ]; then boot_mesg "Configuring upstream name server(s): ${forwarders:1}" ${INFO} echo_ok echo "${forwarders}" > /var/ipfire/red/dns unbound-control -q forward ${forwarders} return 0 fi fi # If forwarders cannot be used we run in recursor mode echo "local recursor" > /var/ipfire/red/dns unbound-control -q forward off } update_hosts() { local enabled address hostname domainname while IFS="," read -r enabled address hostname domainname; do [ "${enabled}" = "on" ] || continue # Build FQDN local fqdn="${hostname}.${domainname}" unbound-control -q local_data "${fqdn} ${LOCAL_TTL} IN A ${address}" done < /var/ipfire/main/hosts } write_interfaces_conf() { ( config_header if [ -n "${GREEN_ADDRESS}" ]; then echo "# GREEN" echo "interface: ${GREEN_ADDRESS}" echo "access-control: $(cidr ${GREEN_NETADDRESS} ${GREEN_NETMASK}) allow" fi if [ -n "${BLUE_ADDRESS}" ]; then echo "# BLUE" echo "interface: ${BLUE_ADDRESS}" echo "access-control: $(cidr ${BLUE_NETADDRESS} ${BLUE_NETMASK}) allow" fi ) > /etc/unbound/interfaces.conf } write_forward_conf() { ( config_header local enabled zone server remark while IFS="," read -r enabled zone server remark; do # Line must be enabled. [ "${enabled}" = "on" ] || continue echo "forward-zone:" echo " name: ${zone}" echo " forward-addr: ${server}" echo done < /var/ipfire/dnsforward/config ) > /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) # In the worst case scenario, unbound can use double the # amount of memory allocated to a cache due to malloc overhead # Large systems with more than 2GB of RAM if [ ${mem} -ge 2048 ]; then mem=128 # Small systems with less than 256MB of RAM elif [ ${mem} -le 256 ]; then mem=8 # Everything else else mem=32 fi ( config_header # We run one thread per processor echo "num-threads: ${processors}" # 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" echo "key-cache-size: $(( ${mem} / 4 ))m" ) > /etc/unbound/tuning.conf } get_memory_amount() { local key val unit while read -r key val unit; do case "${key}" in MemTotal:*) # Convert to MB echo "$(( ${val} / 1024 ))" break ;; esac done < /proc/meminfo } test_name_server() { local ns=${1} # 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 # Return 0 if validating ns_is_validating ${ns} && return 0 local errors for rr in DNSKEY DS RRSIG; do if ! ns_forwards_${rr} ${ns}; 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 # Is DNSSEC-aware return 2 } # Sends an A query to the nameserver w/o DNSSEC ns_is_online() { local ns=${1} dig @${ns} +nodnssec A ${TEST_DOMAIN} >/dev/null } # Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating ns_is_validating() { local ns=${1} dig @${ns} A ${TEST_DOMAIN_FAIL} | grep -q SERVFAIL } # 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} dig @${ns} DNSKEY ${TEST_DOMAIN} | grep -qv SOA } ns_forwards_DS() { local ns=${1} dig @${ns} DS ${TEST_DOMAIN} | grep -qv SOA } ns_forwards_RRSIG() { local ns=${1} dig @${ns} +dnssec A ${TEST_DOMAIN} | grep -q RRSIG } ns_supports_tcp() { local ns=${1} dig @${ns} +tcp A ${TEST_DOMAIN} >/dev/null || return 1 } case "$1" in start) # Print a nicer messagen when unbound is already running if pidofproc -s unbound; then statusproc /usr/sbin/unbound exit 0 fi eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings) # Create control keys at first run if [ ! -r "/etc/unbound/unbound_control.key" ]; then unbound-control-setup -d /etc/unbound &>/dev/null fi # Update configuration files write_tuning_conf write_interfaces_conf write_forward_conf boot_mesg "Starting Unbound DNS Proxy..." loadproc /usr/sbin/unbound || exit $? # Update any known forwarding name servers update_forwarders # Update hosts update_hosts ;; stop) boot_mesg "Stopping Unbound DNS Proxy..." killproc /usr/sbin/unbound ;; restart) $0 stop sleep 1 $0 start ;; status) statusproc /usr/sbin/unbound ;; update-forwarders) update_forwarders ;; 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" ;; esac if ns_supports_tcp ${ns}; then echo "${ns} supports TCP fallback" else echo "${ns} does not support TCP fallback" fi exit ${ret} ;; *) echo "Usage: $0 {start|stop|restart|status|update-forwarders|test-name-server}" exit 1 ;; esac # End $rc_base/init.d/unbound