X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Finitscripts%2Fsystem%2Funbound;h=7bf2ffc2cfed62b215d5813bad7096b4af5ea9c5;hb=d98bbcc8494d4037df2572098fa9ea77f932ce1a;hp=e87f9e10dd6f9cbc86b678dd6574799972c501f0;hpb=6874a5765b887b51e324e1afbddc4516d66a710f;p=people%2Fpmueller%2Fipfire-2.x.git diff --git a/src/initscripts/system/unbound b/src/initscripts/system/unbound index e87f9e10dd..7bf2ffc2cf 100644 --- a/src/initscripts/system/unbound +++ b/src/initscripts/system/unbound @@ -7,46 +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 - # Cache any local zones for 60 seconds LOCAL_TTL=60 -# EDNS buffer size -EDNS_DEFAULT_BUFFER_SIZE=4096 - -# 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}" -} +# 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} @@ -58,10 +24,27 @@ ip_address_revptr() { } read_name_servers() { - local i - for i in 1 2; do - echo "$(/dev/null | xargs echo + # Read name servers from ISP + if [ "${USE_ISP_NAMESERVERS}" = "on" -a "${PROTO}" != "TLS" ]; then + local i + for i in 1 2; do + echo "$(/dev/null + fi + + # 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 + + 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() { @@ -70,135 +53,67 @@ config_header() { echo } -update_forwarders() { - if [ "${USE_FORWARDERS}" = "1" -a -e "/var/ipfire/red/active" ]; 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 - - # Determine EDNS buffer size - local new_edns_buffer_size=${EDNS_DEFAULT_BUFFER_SIZE} - - for ns in ${forwarders}; do - local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns}) - if [ -n "${edns_buffer_size}" ]; then - if [ ${edns_buffer_size} -lt ${new_edns_buffer_size} ]; then - new_edns_buffer_size=${edns_buffer_size} - fi - fi - done - - if [ ${new_edns_buffer_size} -lt ${EDNS_DEFAULT_BUFFER_SIZE} ]; then - boot_mesg "EDNS buffer size reduced to ${new_edns_buffer_size}" ${WARNING} - echo_warning - - unbound-control -q set_option edns-buffer-size: ${new_edns_buffer_size} - fi - - # 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 +bufsize=${new_edns_buffer_size}; 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 -} - -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 - local address - for address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do - [ -n "${address}" ] || continue - [ "${address}" = "1.1.1.1" ] && continue + local address + for address in ${GREEN_ADDRESS} ${BLUE_ADDRESS} ${ORANGE_ADDRESS}; do + [ -n "${address}" ] || continue + [ "${address}" = "1.1.1.1" ] && continue - address=$(ip_address_revptr ${address}) - unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${hostname}" - done -} - -update_hosts() { - local enabled address hostname domainname generateptr - - while IFS="," read -r enabled address hostname domainname generateptr; do - [ "${enabled}" = "on" ] || continue + address=$(ip_address_revptr ${address}) + echo "local-data: \"${address} ${LOCAL_TTL} IN PTR ${HOSTNAME}\"" + done - # Build FQDN - local fqdn="${hostname}.${domainname}" + # Add all hosts + local enabled address hostname domainname generateptr + 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 - local insecure_zones="${INSECURE_ZONES}" + # Enable strict QNAME minimisation + if [ "${QNAME_MIN}" = "strict" ]; then + echo "server:" + echo " qname-minimisation-strict: yes" + echo + fi + + # Force using TCP for upstream servers only + if [ "${PROTO}" = "TCP" ]; then + echo "# Force using TCP for upstream servers only" + echo "server:" + echo " tcp-upstream: yes" + echo + fi + + local insecure_zones="" local enabled zone server servers remark disable_dnssec rest while IFS="," read -r enabled zone servers remark disable_dnssec rest; do @@ -218,35 +133,24 @@ write_forward_conf() { ;; esac - # Reverse-lookup zones must be stubs + echo "stub-zone:" + echo " name: ${zone}" + for server in ${servers//|/ }; do + if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo " stub-addr: ${server}" + else + echo " stub-host: ${server}" + fi + done + echo + + # Make all reverse lookup zones transparent case "${zone}" in *.in-addr.arpa) - echo "stub-zone:" - echo " name: ${zone}" - for server in ${servers//|/ }; do - if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo " stub-addr: ${server}" - else - echo " stub-host: ${server}" - fi - done - echo echo "server:" echo " local-zone: \"${zone}\" transparent" echo ;; - *) - echo "forward-zone:" - echo " name: ${zone}" - for server in ${servers//|/ }; do - if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo " forward-addr: ${server}" - else - echo " forward-host: ${server}" - fi - done - echo - ;; esac done < /var/ipfire/dnsforward/config @@ -257,6 +161,30 @@ 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 } @@ -345,164 +273,286 @@ 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 - - # Determine the maximum edns buffer size that works - local edns_buffer_size=$(ns_determine_edns_buffer_size ${ns}) - if [ -n "${edns_buffer_size}" ]; then - args="${args} +bufsize=${edns_buffer_size}" - fi - - 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 +fix_time_if_dns_fails() { + # Sometimes the first try fails so do it twice + resolve "ping.ipfire.org" &>/dev/null + # If DNS is working, everything is fine + if resolve "ping.ipfire.org" &>/dev/null; then 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 @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null + # 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 } -# Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating -ns_is_validating() { - local ns=${1} - shift - - if ! dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then - return 1 - else - # Determine if NS replies with "ad" data flag if DNSSEC enabled - dig @${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 @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA -} - -ns_forwards_DS() { - local ns=${1} - shift - - dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA -} +resolve() { + local hostname="${1}" + local found=1 -ns_forwards_RRSIG() { - local ns=${1} - shift - - dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG -} - -ns_supports_tcp() { - local ns=${1} - shift - - dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1 -} - -ns_determine_edns_buffer_size() { - local ns=${1} - shift - - local b - for b in 4096 2048 1500 1480 1464 1400 1280 512; do - if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then - echo "${b}" - return 0 + 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 1 + return ${found} } -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 -} +# Sets up Safe Search for various search engines +update_safe_search() { + local google_tlds=( + google.ad + google.ae + google.al + google.am + google.as + google.at + google.az + google.ba + google.be + google.bf + google.bg + google.bi + google.bj + google.bs + google.bt + google.by + google.ca + google.cat + google.cd + google.cf + google.cg + google.ch + google.ci + google.cl + google.cm + google.cn + google.co.ao + google.co.bw + google.co.ck + google.co.cr + google.co.id + google.co.il + google.co.in + google.co.jp + google.co.ke + google.co.kr + google.co.ls + google.com + google.co.ma + google.com.af + google.com.ag + google.com.ai + google.com.ar + google.com.au + google.com.bd + google.com.bh + google.com.bn + google.com.bo + google.com.br + google.com.bz + google.com.co + google.com.cu + google.com.cy + google.com.do + google.com.ec + google.com.eg + google.com.et + google.com.fj + google.com.gh + google.com.gi + google.com.gt + google.com.hk + google.com.jm + google.com.kh + google.com.kw + google.com.lb + google.com.ly + google.com.mm + google.com.mt + google.com.mx + google.com.my + google.com.na + google.com.nf + google.com.ng + google.com.ni + google.com.np + google.com.om + google.com.pa + google.com.pe + google.com.pg + google.com.ph + google.com.pk + google.com.pr + google.com.py + google.com.qa + google.com.sa + google.com.sb + google.com.sg + google.com.sl + google.com.sv + google.com.tj + google.com.tr + google.com.tw + google.com.ua + google.com.uy + google.com.vc + google.com.vn + google.co.mz + google.co.nz + google.co.th + google.co.tz + google.co.ug + google.co.uk + google.co.uz + google.co.ve + google.co.vi + google.co.za + google.co.zm + google.co.zw + google.cv + google.cz + google.de + google.dj + google.dk + google.dm + google.dz + google.ee + google.es + google.fi + google.fm + google.fr + google.ga + google.ge + google.gg + google.gl + google.gm + google.gp + google.gr + google.gy + google.hn + google.hr + google.ht + google.hu + google.ie + google.im + google.iq + google.is + google.it + google.je + google.jo + google.kg + google.ki + google.kz + google.la + google.li + google.lk + google.lt + google.lu + google.lv + google.md + google.me + google.mg + google.mk + google.ml + google.mn + google.ms + google.mu + google.mv + google.mw + google.ne + google.nl + google.no + google.nr + google.nu + google.pl + google.pn + google.ps + google.pt + google.ro + google.rs + google.ru + google.rw + google.sc + google.se + google.sh + google.si + google.sk + google.sm + google.sn + google.so + google.sr + google.st + google.td + google.tg + google.tk + google.tl + google.tm + google.tn + google.to + google.tt + google.vg + google.vu + google.ws + ) + + # Cleanup previous settings + unbound-control local_zone_remove "bing.com" >/dev/null + unbound-control local_zone_remove "duckduckgo.com" >/dev/null + unbound-control local_zone_remove "yandex.com" >/dev/null + unbound-control local_zone_remove "yandex.ru" >/dev/null + unbound-control local_zone_remove "youtube.com" >/dev/null + + local domain + for domain in ${google_tlds[@]}; do + unbound-control local_zone_remove "${domain}" + done >/dev/null + + # Nothing to do if safe search is not enabled + if [ "${ENABLE_SAFE_SEARCH}" != "on" ]; then + return 0 + fi -can_resolve_root() { - local ns - for ns in $(get_root_nameservers); do - if dig @${ns} +dnssec SOA . $@ >/dev/null; then - return 0 - fi + # Bing + unbound-control bing.com transparent >/dev/null + for address in $(resolve "strict.bing.com"); do + unbound-control local_data "www.bing.com ${LOCAL_TTL} IN A ${address}" + done >/dev/null + + # DuckDuckGo + unbound-control local_zone duckduckgo.com typetransparent >/dev/null + for address in $(resolve "safe.duckduckgo.com"); do + unbound-control local_data "duckduckgo.com ${LOCAL_TTL} IN A ${address}" + done >/dev/null + + # Google + local addresses="$(resolve "forcesafesearch.google.com")" + 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}" + done >/dev/null 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 + # Yandex + for domain in yandex.com yandex.ru; do + unbound-control local_zone "${domain}" typetransparent >/dev/null + for address in $(resolve "familysearch.${domain}"); do + unbound-control local_data "${domain} ${LOCAL_TTL} IN A ${address}" + done >/dev/null + done - unbound-control -q set_option val-permissive-mode: yes -} + # YouTube + unbound-control local_zone youtube.com transparent >/dev/null + for address in $(resolve "restrictmoderate.youtube.com"); do + unbound-control local_data "www.youtube.com ${LOCAL_TTL} IN A ${address}" + done >/dev/null -fix_time_if_dns_fail() { - # If DNS still not work try to init ntp with - # hardcoded ntp.ipfire.org (81.3.27.46) - if [ -e /var/ipfire/red/active ]; 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 + return 0 } case "$1" in @@ -513,25 +563,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 - - # Update hosts - update_hosts - - fix_time_if_dns_fail + # Install Safe Search rules when the system is already online + if [ -e "/var/ipfire/red/active" ]; then + update_safe_search + fi ;; stop) @@ -544,63 +587,42 @@ case "$1" in sleep 1 $0 start ;; + reload|remove-forwarders|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 + # Call unbound-control and perform the reload + /usr/sbin/unbound-control -q reload - update_forwarders + # Dummy Resolve to wait for unbound + resolve "ping.ipfire.org" &>/dev/null - unbound-control flush_negative > /dev/null - unbound-control flush_bogus > /dev/null + if [ "$1" = "update-forwarders" ]; then + # Make sure DNS works at this point + fix_time_if_dns_fails + fi - fix_time_if_dns_fail + # Update Safe Search rules if the system is online. + if [ -e "/var/ipfire/red/active" ]; then + update_safe_search + fi ;; - 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" - fi + status) + statusproc /usr/sbin/unbound + ;; - edns_buffer_size=$(ns_determine_edns_buffer_size ${ns}) - if [ -n "${edns_buffer_size}" ]; then - echo "EDNS buffer size for ${ns}: ${edns_buffer_size}" - fi + # Make sure DNS works at this point + fix_time_if_dns_fails + ;; - exit ${ret} + resolve) + resolve "${2}" || exit $? ;; *) - echo "Usage: $0 {start|stop|restart|status|update-forwarders|test-name-server}" + echo "Usage: $0 {start|stop|restart|reload|status|resolve|update-forwarders|remove-forwarders}" exit 1 ;; esac