#!/bin/sh ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2007-2022 IPFire Team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### . /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 local enabled address hostname domainname generateptr # Find all unique domain names while IFS="," read -r enabled address hostname domainname generateptr; do [ "${enabled}" = "on" ] || continue # Skip empty domainnames [ "${domainname}" = "" ] && continue echo "local-zone: ${domainname} transparent" done < /var/ipfire/main/hosts | sort -u # Add all hosts 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 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 # 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 "0.ipfire.pool.ntp.org" &>/dev/null || \ resolve "1.ipfire.pool.ntp.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 time.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 if [ "${ENABLE_SAFE_SEARCH}" = "on" ]; then # 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 if [ "${ENABLE_SAFE_SEARCH_YOUTUBE}" = "on" ]; then 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 fi fi 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|update-forwarders) # Update configuration files write_forward_conf write_hosts_conf # Call unbound-control and perform the reload /usr/sbin/unbound-control -q reload # Dummy Resolve to wait for unbound resolve "ping.ipfire.org" &>/dev/null if [ "$1" = "update-forwarders" ]; then # Make sure DNS works at this point fix_time_if_dns_fails fi # Update Safe Search rules if the system is online. if [ -e "/var/ipfire/red/active" ]; then update_safe_search fi ;; status) statusproc /usr/sbin/unbound ;; resolve) resolve "${2}" || exit $? ;; *) echo "Usage: $0 {start|stop|restart|reload|status|resolve|update-forwarders}" exit 1 ;; esac