#!/bin/sh # Begin $rc_base/init.d/unbound # Description : Unbound DNS resolver boot script for IPfire # Author : Marcel Lorenz . /etc/sysconfig/rc . ${rc_functions} # 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 } write_hosts_conf() { ( config_header # 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 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}) echo "local-data: \"${address} ${LOCAL_TTL} IN PTR ${HOSTNAME}\"" done # Add all 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}" 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 user requested not to do so [ "${generateptr}" = "off" ] && continue # 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 # 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 # 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) # 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() { # 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 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=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 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_hosts_conf write_forward_conf boot_mesg "Starting Unbound DNS Proxy..." loadproc /usr/sbin/unbound || exit $? # Install Safe Search rules when the system is already online if [ -e "/var/ipfire/red/active" ]; then update_safe_search fi ;; stop) boot_mesg "Stopping Unbound DNS Proxy..." killproc /usr/sbin/unbound ;; restart) $0 stop sleep 1 $0 start ;; reload|remove-forwarders) # Update configuration files write_forward_conf write_hosts_conf # Update Safe Search rules if the system is online. if [ -e "/var/ipfire/red/active" ]; then update_safe_search fi # Call unbound-control and perform the reload /usr/sbin/unbound-control -q reload ;; status) statusproc /usr/sbin/unbound ;; update-forwarders) $0 reload # Make sure DNS works at this point fix_time_if_dns_fails ;; resolve) resolve "${2}" || exit $? ;; *) echo "Usage: $0 {start|stop|restart|reload|status|resolve|update-forwarders|remove-forwarders}" exit 1 ;; esac # End $rc_base/init.d/unbound