]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/initscripts/system/unbound
unbound: Update setting Safe Search redirects
[ipfire-2.x.git] / src / initscripts / system / unbound
index 6ea49f5d809cf685e9123a0b3af36519f0731e00..bb78fd18aeaba10259eac28e3c80a445dfbdb7ce 100644 (file)
@@ -12,41 +12,11 @@ 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)
 
 ip_address_revptr() {
        local addr=${1}
@@ -61,97 +31,29 @@ read_name_servers() {
        local i
        for i in 1 2; do
                echo "$(</var/ipfire/red/dns${i})"
-       done | xargs echo
+       done 2>/dev/null | xargs echo
 }
 
-config_header() {
-       echo "# This file is automatically generated and any changes"
-       echo "# will be overwritten. DO NOT EDIT!"
-       echo
-}
+check_red_has_carrier_and_ip() {
+       # Interface configured ?
+       [ ! -e "/var/ipfire/red/iface" ] && return 0;
 
-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
+       # Interface present ?
+       [ ! -e "/sys/class/net/$(</var/ipfire/red/iface)" ] && return 0;
 
-               # Determine EDNS buffer size
-               local new_edns_buffer_size=${EDNS_DEFAULT_BUFFER_SIZE}
+       # has carrier ?
+       [ ! "$(</sys/class/net/$(</var/ipfire/red/iface)/carrier)" = "1" ] && return 0;
 
-               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
+       # has ip ?
+       [ "$(ip address show dev $(</var/ipfire/red/iface) | grep "inet")" = "" ] && return 0;
 
-                       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
-
-                               echo "${broken_forwarders}" > /var/ipfire/red/dns
-                               unbound-control -q forward ${broken_forwarders}
-                               return 0
-                       fi
-               fi
-       fi
+       return 1;
+}
 
-       # If forwarders cannot be used we run in recursor mode
-       echo "local recursor" > /var/ipfire/red/dns
-       unbound-control -q forward off
+config_header() {
+       echo "# This file is automatically generated and any changes"
+       echo "# will be overwritten. DO NOT EDIT!"
+       echo
 }
 
 own_hostname() {
@@ -172,9 +74,9 @@ own_hostname() {
 }
 
 update_hosts() {
-       local enabled address hostname domainname
+       local enabled address hostname domainname generateptr
 
-       while IFS="," read -r enabled address hostname domainname; do
+       while IFS="," read -r enabled address hostname domainname generateptr; do
                [ "${enabled}" = "on" ] || continue
 
                # Build FQDN
@@ -185,6 +87,9 @@ update_hosts() {
                # 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
+
                # Add RDNS
                address=$(ip_address_revptr ${address})
                unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${fqdn}"
@@ -195,10 +100,18 @@ write_forward_conf() {
        (
                config_header
 
-               local insecure_zones="${INSECURE_ZONES}"
+               # 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 enabled zone server remark
-               while IFS="," read -r enabled zone server remark; do
+               local insecure_zones=""
+
+               local enabled zone server servers remark disable_dnssec rest
+               while IFS="," read -r enabled zone servers remark disable_dnssec rest; do
                        # Line must be enabled.
                        [ "${enabled}" = "on" ] || continue
 
@@ -208,12 +121,32 @@ write_forward_conf() {
                                *.local)
                                        insecure_zones="${insecure_zones} ${zone}"
                                        ;;
+                               *)
+                                       if [ "${disable_dnssec}" = "on" ]; then
+                                               insecure_zones="${insecure_zones} ${zone}"
+                                       fi
+                                       ;;
                        esac
 
-                       echo "forward-zone:"
+                       echo "stub-zone:"
                        echo "  name: ${zone}"
-                       echo "  forward-addr: ${server}"
+                       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 "server:"
+                                       echo "  local-zone: \"${zone}\" transparent"
+                                       echo
+                                       ;;
+                       esac
                done < /var/ipfire/dnsforward/config
 
                if [ -n "${insecure_zones}" ]; then
@@ -223,6 +156,30 @@ write_forward_conf() {
                                echo "  domain-insecure: ${zone}"
                        done
                fi
+
+               echo "forward-zone:"
+               echo "  name: \".\""
+
+               # Force using TLS only
+               if [ "${PROTO}" = "TLS" ]; then
+                       echo "  forward-tls-upstream: yes"
+               fi
+
+               # Add upstream name servers
+               local id address tls_hostname enabled remark
+               while IFS="," read -r id address tls_hostname enabled remark; do
+                       # Skip disabled servers
+                       [ "${enabled}" != "enabled" ] && continue
+
+                       # Set DNS server
+                       if [ "${PROTO}" = "TLS" ]; then
+                               if [ -n "${tls_hostname}" ]; then
+                                       echo "  forward-addr: ${address}@853#${tls_hostname}"
+                               fi
+                       else
+                               echo "  forward-addr: ${address}"
+                       fi
+               done < /var/ipfire/dns/servers
        ) > /etc/unbound/forward.conf
 }
 
@@ -244,17 +201,29 @@ write_tuning_conf() {
        # In the worst case scenario, unbound can use double the
        # amount of memory allocated to a cache due to malloc overhead
 
+       # Even larger systems with more than 8GB of RAM
+       if [ ${mem} -ge 8192 ]; then
+               mem=1024
+
+       # Extra large systems with more than 4GB of RAM
+       elif [ ${mem} -ge 4096 ]; then
+               mem=512
+
        # Large systems with more than 2GB of RAM
-       if [ ${mem} -ge 2048 ]; then
+       elif [ ${mem} -ge 2048 ]; then
+               mem=256
+
+       # Medium systems with more than 1GB of RAM
+       elif [ ${mem} -ge 1024 ]; then
                mem=128
 
        # Small systems with less than 256MB of RAM
        elif [ ${mem} -le 256 ]; then
-               mem=8
+               mem=16
 
        # Everything else
        else
-               mem=32
+               mem=64
        fi
 
        (
@@ -262,6 +231,7 @@ write_tuning_conf() {
 
                # We run one thread per processor
                echo "num-threads: ${processors}"
+               echo "so-reuseport: yes"
 
                # Adjust number of slabs
                echo "infra-cache-slabs: ${slabs}"
@@ -277,6 +247,10 @@ write_tuning_conf() {
                # Increase parallel queries
                echo "outgoing-range: 8192"
                echo "num-queries-per-thread: 4096"
+
+               # Use larger send/receive buffers
+               echo "so-sndbuf: 4m"
+               echo "so-rcvbuf: 4m"
        ) > /etc/unbound/tuning.conf
 }
 
@@ -294,141 +268,282 @@ 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}"
+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
-       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 @${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
-
-       dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL
-}
+resolve() {
+       local hostname="${1}"
 
-# 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
-}
-
-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
+                       echo "${answer}"
                fi
        done
-
-       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
-}
+# 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)
-
-       # Don't do anything if DNSSEC is already activated
-       [ "${status}" = "no" ] && return 0
+       # 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
 
-       # 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 .
-}
+       # 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
 
-disable_dnssec() {
-       unbound-control -q set_option val-permissive-mode: yes
+       return 0
 }
 
 case "$1" in
@@ -441,11 +556,6 @@ case "$1" in
 
                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_forward_conf
@@ -456,11 +566,15 @@ case "$1" in
                # 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)
@@ -479,52 +593,26 @@ case "$1" in
                ;;
 
        update-forwarders)
-               # Do not try updating forwarders when unbound is not running
-               if ! pgrep unbound &>/dev/null; then
-                       exit 0
-               fi
+               : # XXX must set ISP name servers if necessary
 
-               update_forwarders
+               # Update Safe Search settings
+               update_safe_search
                ;;
 
-       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
+       remove-forwarders)
+               : # XXX must remove ISP name servers
+               ;;
 
-               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
+       resolve)
+               resolve "${2}"
+               ;;
 
-               exit ${ret}
+       update-safe-search)
+               update_safe_search
                ;;
 
        *)
-               echo "Usage: $0 {start|stop|restart|status|update-forwarders|test-name-server}"
+               echo "Usage: $0 {start|stop|restart|status|resolve|update-forwarders|remove-forwarders|update-safe-search}"
                exit 1
                ;;
 esac