#!/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" # Cache any local zones for 60 seconds LOCAL_TTL=60 # 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} local a1 a2 a3 a4 IFS=. read -r a1 a2 a3 a4 <<< ${addr} echo "${a4}.${a3}.${a2}.${a1}.in-addr.arpa" } read_name_servers() { # 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() { echo "# This file is automatically generated and any changes" echo "# will be overwritten. DO NOT EDIT!" echo } 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 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 # Build FQDN local fqdn="${hostname}.${domainname}" unbound-control -q 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 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 } write_forward_conf() { ( config_header # 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 # Line must be enabled. [ "${enabled}" = "on" ] || continue # Zones that end with .local are commonly used for internal # zones and therefore not signed case "${zone}" in *.local) insecure_zones="${insecure_zones} ${zone}" ;; *) if [ "${disable_dnssec}" = "on" ]; then insecure_zones="${insecure_zones} ${zone}" fi ;; esac 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 "server:" echo " local-zone: \"${zone}\" transparent" echo ;; esac done < /var/ipfire/dnsforward/config if [ -n "${insecure_zones}" ]; then echo "server:" for zone in ${insecure_zones}; do 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 ns for ns in $(read_name_servers); do echo " forward-addr: ${ns}" done ) > /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 # 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 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=16 # Everything else else mem=64 fi ( 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" echo "key-cache-size: $(( ${mem} / 4 ))m" # 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 } 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 } fix_time_if_dns_fails() { # If DNS is working, everything is fine if resolve "ping.ipfire.org" &>/dev/null; then return 0 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 answer for answer in $(dig +short A "${hostname}"); do # Filter out non-IP addresses if [[ ! "${answer}" =~ \.$ ]]; then echo "${answer}" fi done } update_forwarders() { # Do nothing when we do not use the ISP name servers [ "${USE_ISP_NAMESERVERS}" != "on" ] && return 0 # We cannot update anything when using TLS # Unbound will then try to connect to the servers using UDP on port 853 [ "${PROTO}" = "TLS" ] && return 0 # Update unbound about the new servers local nameservers=( $(read_name_servers) ) if [ -n "${nameservers[*]}" ]; then unbound-control -q forward "${nameservers[@]}" else unbound-control -q forward off fi } # 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 # 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 # 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 # 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 return 0 } 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 # Update configuration files write_tuning_conf write_forward_conf boot_mesg "Starting Unbound DNS Proxy..." loadproc /usr/sbin/unbound || exit $? # Make own hostname resolveable own_hostname # 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 ;; stop) boot_mesg "Stopping Unbound DNS Proxy..." killproc /usr/sbin/unbound ;; restart) $0 stop sleep 1 $0 start ;; reload) # Update configuration files write_tuning_conf write_forward_conf # Update Safe Search rules if the system is online. if [ -e "/var/ipfire/red/active" ]; then update_safe_search fi # Update hosts. update_hosts # Call unbound-control and perform the reload /usr/sbin/unbound-control -q reload ;; status) statusproc /usr/sbin/unbound ;; update-forwarders) update_forwarders # Make sure DNS works at this point fix_time_if_dns_fails # Update Safe Search settings update_safe_search ;; remove-forwarders) update_forwarders ;; resolve) resolve "${2}" ;; update-safe-search) update_safe_search ;; *) echo "Usage: $0 {start|stop|restart|reload|status|resolve|update-forwarders|remove-forwarders|update-safe-search}" exit 1 ;; esac # End $rc_base/init.d/unbound