Index: head/libexec/rc/rc.d/local_unbound =================================================================== --- head/libexec/rc/rc.d/local_unbound (revision 339423) +++ head/libexec/rc/rc.d/local_unbound (revision 339424) @@ -1,115 +1,121 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: local_unbound # REQUIRE: FILESYSTEMS netif resolv # BEFORE: NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="local_unbound" desc="Local caching forwarding resolver" rcvar="local_unbound_enable" command="/usr/sbin/local-unbound" extra_commands="anchor configtest reload setup" start_precmd="local_unbound_prestart" start_postcmd="local_unbound_poststart" reload_precmd="local_unbound_configtest" anchor_cmd="local_unbound_anchor" configtest_cmd="local_unbound_configtest" setup_cmd="local_unbound_setup" pidfile="/var/run/${name}.pid" load_rc_config $name : ${local_unbound_workdir:=/var/unbound} : ${local_unbound_config:=${local_unbound_workdir}/unbound.conf} : ${local_unbound_flags:="-c ${local_unbound_config}"} : ${local_unbound_forwardconf:=${local_unbound_workdir}/forward.conf} : ${local_unbound_controlconf:=${local_unbound_workdir}/control.conf} : ${local_unbound_anchor:=${local_unbound_workdir}/root.key} : ${local_unbound_forwarders:=} +: ${local_unbound_tls:=} do_as_unbound() { echo "$@" | su -m unbound } # # Retrieve or update the DNSSEC root anchor # local_unbound_anchor() { do_as_unbound ${command}-anchor -a ${local_unbound_anchor} # we can't trust the exit code - check if the file exists [ -f ${local_unbound_anchor} ] } # # Check the unbound configuration file # local_unbound_configtest() { do_as_unbound ${command}-checkconf ${local_unbound_config} } # # Create the unbound configuration file and update resolv.conf to # point to unbound. # local_unbound_setup() { + local tls_flag + if checkyesno local_unbound_tls ; then + tls_flag="-t" + fi echo "Performing initial setup." ${command}-setup -n \ -u unbound \ -w ${local_unbound_workdir} \ -c ${local_unbound_config} \ -f ${local_unbound_forwardconf} \ -o ${local_unbound_controlconf} \ -a ${local_unbound_anchor} \ + ${tls_flag} \ ${local_unbound_forwarders} } # # Before starting, check that the configuration file and root anchor # exist. If not, attempt to generate them. # local_unbound_prestart() { # Create configuration file if [ ! -f ${local_unbound_config} ] ; then run_rc_command setup fi # Retrieve DNSSEC root key if [ ! -f ${local_unbound_anchor} ] ; then run_rc_command anchor fi } # # After starting, wait for Unbound to report that it is ready to avoid # race conditions with services which require functioning DNS. # local_unbound_poststart() { local retry=5 echo -n "Waiting for nameserver to start..." until "${command}-control" status | grep -q "is running" ; do if [ $((retry -= 1)) -eq 0 ] ; then echo " giving up" return 1 fi echo -n "." sleep 1 done echo " good" } load_rc_config $name run_rc_command "$1" Index: head/usr.sbin/unbound/setup/local-unbound-setup.sh =================================================================== --- head/usr.sbin/unbound/setup/local-unbound-setup.sh (revision 339423) +++ head/usr.sbin/unbound/setup/local-unbound-setup.sh (revision 339424) @@ -1,460 +1,496 @@ #!/bin/sh #- # SPDX-License-Identifier: BSD-2-Clause-FreeBSD # # Copyright (c) 2013 Dag-Erling Smørgrav # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # +D="${DESTDIR}" +echo "destination: ${D}" + # # Configuration variables # user="" unbound_conf="" forward_conf="" lanzones_conf="" control_conf="" control_socket="" workdir="" confdir="" chrootdir="" anchor="" pidfile="" resolv_conf="" resolvconf_conf="" service="" start_unbound="" +use_tls="" forwarders="" # # Global variables # self=$(basename $(realpath "$0")) +bkdir=/var/backups bkext=$(date "+%Y%m%d.%H%M%S") # +# Regular expressions +# +RE_octet="([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])" +RE_ipv4="(${RE_octet}(\\.${RE_octet}){3})" +RE_word="([0-9A-Fa-f]{1,4})" +RE_ipv6="((${RE_word}:){1,}(:|(:${RE_word})*)|::1)" +RE_port="([1-9][0-9]{0,3}|[1-5][0-9]{4,4}|6([0-4][0-9]{3}|5([0-4][0-9]{2}|5([0-2][0-9]|3[0-5]))))" +RE_dnsname="([0-9A-Za-z-]{1,}(\\.[0-9A-Za-z-]{1,})*\\.?)" +RE_forward_addr="((${RE_ipv4}|${RE_ipv6})(@${RE_port})?)" +RE_forward_name="(${RE_dnsname}(@${RE_port})?)" +RE_forward_tls="(${RE_forward_addr}(#${RE_dnsname})?)" + +# # Set default values for unset configuration variables. # set_defaults() { : ${user:=unbound} : ${workdir:=/var/unbound} : ${confdir:=${workdir}/conf.d} : ${unbound_conf:=${workdir}/unbound.conf} : ${forward_conf:=${workdir}/forward.conf} : ${lanzones_conf:=${workdir}/lan-zones.conf} : ${control_conf:=${workdir}/control.conf} : ${control_socket:=/var/run/local_unbound.ctl} : ${anchor:=${workdir}/root.key} : ${pidfile:=/var/run/local_unbound.pid} : ${resolv_conf:=/etc/resolv.conf} : ${resolvconf_conf:=/etc/resolvconf.conf} : ${service:=local_unbound} : ${start_unbound:=yes} + : ${use_tls:=no} } # # Verify that the configuration files are inside the working # directory, and if so, set the chroot directory accordingly. # set_chrootdir() { chrootdir="${workdir}" for file in "${unbound_conf}" "${forward_conf}" \ "${lanzones_conf}" "${control_conf}" "${anchor}" ; do if [ "${file#${workdir%/}/}" = "${file}" ] ; then echo "warning: ${file} is outside ${workdir}" >&2 chrootdir="" fi done if [ -z "${chrootdir}" ] ; then echo "warning: disabling chroot" >&2 fi } # # Scan through /etc/resolv.conf looking for uncommented nameserver # lines that don't point to localhost and return their values. # get_nameservers() { while read line ; do local bareline=${line%%\#*} local key=${bareline%% *} local value=${bareline#* } case ${key} in nameserver) case ${value} in 127.0.0.1|::1|localhost|localhost.*) ;; *) echo "${value}" ;; esac ;; esac done } # # Scan through /etc/resolv.conf looking for uncommented nameserver # lines. Comment out any that don't point to localhost. Finally, # append a nameserver line that points to localhost, if there wasn't # one already, and enable the edns0 option. # gen_resolv_conf() { local localhost=no local edns0=no while read line ; do local bareline=${line%%\#*} local key=${bareline%% *} local value=${bareline#* } case ${key} in nameserver) case ${value} in 127.0.0.1|::1|localhost|localhost.*) localhost=yes ;; *) echo -n "# " ;; esac ;; options) case ${value} in *edns0*) edns0=yes ;; esac ;; esac echo "${line}" done if [ "${localhost}" = "no" ] ; then echo "nameserver 127.0.0.1" fi if [ "${edns0}" = "no" ] ; then echo "options edns0" fi } # # Boilerplate # do_not_edit() { echo "# This file was generated by $self." echo "# Modifications will be overwritten." } # # Generate resolvconf.conf so it updates forward.conf in addition to # resolv.conf. Note "in addition to" rather than "instead of", # because we still want it to update the domain name and search path # if they change. Setting name_servers to "127.0.0.1" ensures that # the libc resolver will try unbound first. # gen_resolvconf_conf() { local style="$1" do_not_edit echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" if [ "${style}" = "dynamic" ] ; then echo "unbound_conf=\"${forward_conf}\"" echo "unbound_pid=\"${pidfile}\"" echo "unbound_service=\"${service}\"" # resolvconf(8) likes to restart rather than reload echo "unbound_restart=\"service ${service} reload\"" else echo "# Static DNS configuration" fi } # # Generate forward.conf # gen_forward_conf() { do_not_edit echo "forward-zone:" echo " name: ." - for forwarder ; do - if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then - echo " forward-addr: ${forwarder}" - else - echo " forward-host: ${forwarder}" - fi - done + for forwarder ; do echo "${forwarder}" ; done | + if [ "${use_tls}" = "yes" ] ; then + echo " forward-tls-upstream: yes" + sed -nE \ + -e "s/^(${RE_forward_tls})$/ forward-addr: \\1/p" + else + sed -nE \ + -e "s/^${RE_forward_addr}\$/ forward-addr: \\1/p" \ + -e "s/^${RE_forward_name}\$/ forward-host: \\1/p" + fi } # # Generate lan-zones.conf # gen_lanzones_conf() { do_not_edit echo "server:" echo " # Unblock reverse lookups for LAN addresses" echo " unblock-lan-zones: yes" echo " insecure-lan-zones: yes" } # # Generate control.conf # gen_control_conf() { do_not_edit echo "remote-control:" echo " control-enable: yes" echo " control-interface: ${control_socket}" echo " control-use-cert: no" } # # Generate unbound.conf # gen_unbound_conf() { do_not_edit echo "server:" echo " username: ${user}" echo " directory: ${workdir}" echo " chroot: ${chrootdir}" echo " pidfile: ${pidfile}" echo " auto-trust-anchor-file: ${anchor}" + if [ "${use_tls}" = "yes" ] ; then + echo " tls-cert-bundle: /etc/ssl/cert.pem" + fi echo "" if [ -f "${forward_conf}" ] ; then echo "include: ${forward_conf}" fi if [ -f "${lanzones_conf}" ] ; then echo "include: ${lanzones_conf}" fi if [ -f "${control_conf}" ] ; then echo "include: ${control_conf}" fi if [ -d "${confdir}" ] ; then echo "include: ${confdir}/*.conf" fi } # # Rename a file we are about to replace. # backup() { local file="$1" - if [ -f "${file}" ] ; then - local bkfile="${file}.${bkext}" + if [ -f "${D}${file}" ] ; then + local bkfile="${bkdir}/${file##*/}.${bkext}" echo "Original ${file} saved as ${bkfile}" - mv "${file}" "${bkfile}" + mv "${D}${file}" "${D}${bkfile}" fi } # +# Wrapper for mktemp which respects DESTDIR +# +tmp() { + local file="$1" + mktemp -u "${D}${file}.XXXXX" +} + +# # Replace one file with another, making a backup copy of the first, # but only if the new file is different from the old. # replace() { local file="$1" local newfile="$2" - if [ ! -f "${file}" ] ; then + if [ ! -f "${D}${file}" ] ; then echo "${file} created" - mv "${newfile}" "${file}" - elif ! cmp -s "${file}" "${newfile}" ; then + mv "${newfile}" "${D}${file}" + elif ! cmp -s "${D}${file}" "${newfile}" ; then backup "${file}" - mv "${newfile}" "${file}" + mv "${newfile}" "${D}${file}" else echo "${file} not modified" rm "${newfile}" fi } # # Print usage message and exit # usage() { exec >&2 echo "usage: $self [options] [forwarder ...]" echo "options:" echo " -n do not start unbound" echo " -a path full path to trust anchor file" echo " -C path full path to additional configuration directory" echo " -c path full path to unbound configuration file" echo " -f path full path to forwarding configuration" echo " -O path full path to remote control socket" echo " -o path full path to remote control configuration" echo " -p path full path to pid file" echo " -R path full path to resolvconf.conf" echo " -r path full path to resolv.conf" echo " -s service name of unbound service" echo " -u user user to run unbound as" echo " -w path full path to working directory" exit 1 } # # Main # main() { umask 022 # # Parse and validate command-line options # - while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do + while getopts "a:C:c:f:no:p:R:r:s:tu:w:" option ; do case $option in a) anchor="$OPTARG" ;; C) confdir="$OPTARG" ;; c) unbound_conf="$OPTARG" ;; f) forward_conf="$OPTARG" ;; n) start_unbound="no" ;; O) control_socket="$OPTARG" ;; o) control_conf="$OPTARG" ;; p) pidfile="$OPTARG" ;; R) resolvconf_conf="$OPTARG" ;; r) resolv_conf="$OPTARG" ;; s) service="$OPTARG" ;; + t) + use_tls="yes" + ;; u) user="$OPTARG" ;; w) workdir="$OPTARG" ;; *) usage ;; esac done shift $((OPTIND-1)) set_defaults # # Get the list of forwarders, either from the command line or # from resolv.conf. # forwarders="$@" case "${forwarders}" in [Nn][Oo][Nn][Ee]) forwarders="none" style=recursing ;; "") echo "Extracting forwarders from ${resolv_conf}." - forwarders=$(get_nameservers <"${resolv_conf}") + forwarders=$(get_nameservers <"${D}${resolv_conf}") style=dynamic ;; *) style=static ;; esac # # Generate forward.conf. # if [ -z "${forwarders}" ] ; then echo -n "No forwarders found in ${resolv_conf##*/}, " if [ -f "${forward_conf}" ] ; then echo "using existing ${forward_conf##*/}." else echo "unbound will recurse." fi elif [ "${forwarders}" = "none" ] ; then echo "Forwarding disabled, unbound will recurse." backup "${forward_conf}" else - local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") + local tmp_forward_conf=$(tmp "${forward_conf}") gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}" replace "${forward_conf}" "${tmp_forward_conf}" fi # # Generate lan-zones.conf. # - local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX") + local tmp_lanzones_conf=$(tmp "${lanzones_conf}") gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}" replace "${lanzones_conf}" "${tmp_lanzones_conf}" # # Generate control.conf. # - local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX") + local tmp_control_conf=$(tmp "${control_conf}") gen_control_conf | unexpand >"${tmp_control_conf}" replace "${control_conf}" "${tmp_control_conf}" # # Generate unbound.conf. # - local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") + local tmp_unbound_conf=$(tmp "${unbound_conf}") set_chrootdir gen_unbound_conf | unexpand >"${tmp_unbound_conf}" replace "${unbound_conf}" "${tmp_unbound_conf}" # # Start unbound, unless requested not to. Stop immediately if # it is not enabled so we don't end up with a resolv.conf that # points into nothingness. We could "onestart" it, but it # wouldn't stick. # if [ "${start_unbound}" = "no" ] ; then # skip elif ! service "${service}" enabled ; then echo "Please enable $service in rc.conf(5) and try again." return 1 elif ! service "${service}" restart ; then echo "Failed to start $service." return 1 fi # # Rewrite resolvconf.conf so resolvconf updates forward.conf # instead of resolv.conf. # - local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") + local tmp_resolvconf_conf=$(tmp "${resolvconf_conf}") gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}" replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" # # Finally, rewrite resolv.conf. # - local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") - gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}" + local tmp_resolv_conf=$(tmp "${resolv_conf}") + gen_resolv_conf <"${D}${resolv_conf}" | unexpand >"${tmp_resolv_conf}" replace "${resolv_conf}" "${tmp_resolv_conf}" } main "$@"