Add a setup script for unbound(8) called local-unbound-setup. It

generates a configuration suitable for running unbound as a caching
forwarding resolver, and configures resolvconf(8) to update unbound's
list of forwarders in addition to /etc/resolv.conf.  The initial list
is taken from the existing resolv.conf, which is rewritten to point to
localhost.  Alternatively, a list of forwarders can be provided on the
command line.

To assist this script, add an rc.subr command called "enabled" which
does nothing except return 0 if the service is enabled and 1 if it is
not, without going through the usual checks.  We should consider doing
the same for "status", which is currently pointless.

Add an rc script for unbound, called local_unbound.  If there is no
configuration file, the rc script runs local-unbound-setup to generate
one.

Note that these scripts place the unbound configuration files in
/var/unbound rather than /etc/unbound.  This is necessary so that
unbound can reload its configuration while chrooted.  We should
probably provide symlinks in /etc.

Approved by:	re (blanket)
This commit is contained in:
Dag-Erling Smørgrav 2013-09-23 04:36:51 +00:00
parent dd5f1297c6
commit 49cede74ee
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=255809
11 changed files with 488 additions and 4 deletions

View File

@ -270,6 +270,7 @@ hastd_enable="NO" # Run the HAST daemon (YES/NO).
hastd_program="/sbin/hastd" # path to hastd, if you want a different one.
hastd_flags="" # Optional flags to hastd.
ctld_enable="NO" # CAM Target Layer / iSCSI target daemon.
local_unbound_enable="NO" # local caching resolver
#
# named. It may be possible to run named in a sandbox, man security for
# details.

View File

@ -150,6 +150,7 @@ FILES= DAEMON \
tmp \
${_ubthidhci} \
ugidfw \
${_unbound} \
${_utx} \
var \
virecover \
@ -184,6 +185,10 @@ _nscd= nscd
_ubthidhci= ubthidhci
.endif
.if ${MK_UNBOUND} != "no"
_unbound= local_unbound
.endif
.if ${MK_UTMPX} != "no"
_utx= utx
.endif

91
etc/rc.d/local_unbound Executable file
View File

@ -0,0 +1,91 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: local_unbound
# REQUIRE: SERVERS cleanvar
# KEYWORD: shutdown
. /etc/rc.subr
name="local_unbound"
desc="local caching forwarding resolver"
rcvar="local_unbound_enable"
command="/usr/sbin/unbound"
extra_commands="anchor configtest reload setup"
start_precmd="local_unbound_prestart"
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"
: ${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_anchor:=${local_unbound_workdir}/root.key}
: ${local_unbound_forwarders:=}
load_rc_config $name
do_as_unbound()
{
echo "$@" | su -m unbound
}
#
# Retrieve or update the DNSSEC root anchor
#
local_unbound_anchor()
{
do_as_unbound /usr/sbin/unbound-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 /usr/sbin/unbound-checkconf ${local_unbound_config}
}
#
# Create the unbound configuration file and update resolv.conf to
# point to unbound.
#
local_unbound_setup()
{
echo "Performing initial setup."
/usr/sbin/local-unbound-setup -n \
-u unbound \
-w ${local_unbound_workdir} \
-c ${local_unbound_config} \
-f ${local_unbound_forwardconf} \
-a ${local_unbound_anchor} \
${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
}
load_rc_config $name
run_rc_command "$1"

View File

@ -546,6 +546,8 @@ check_startmsgs()
#
# rcvar Display what rc.conf variable is used (if any).
#
# enabled Return true if the service is enabled.
#
# Variables available to methods, and after run_rc_command() has
# completed:
#
@ -614,7 +616,7 @@ run_rc_command()
eval _override_command=\$${name}_program
command=${_override_command:-$command}
_keywords="start stop restart rcvar $extra_commands"
_keywords="start stop restart rcvar enabled $extra_commands"
rc_pid=
_pidcmd=
_procname=${procname:-${command}}
@ -635,6 +637,11 @@ run_rc_command()
rc_usage $_keywords
fi
if [ "$rc_arg" = "enabled" ] ; then
checkyesno ${rcvar}
return $?
fi
if [ -n "$flags" ]; then # allow override from environment
rc_flags=$flags
else

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 10, 2013
.Dd September 23, 2013
.Dt RC.CONF 5
.Os
.Sh NAME
@ -2041,6 +2041,13 @@ is set to
.Dq Li YES ,
these are the flags to pass to
.Xr hastd 8 .
.It Va local_unbound_enable
.Pq Vt bool
If set to
.Dq Li YES ,
run the
.Xr unbound 8
daemon as a local caching resolver.
.It Va named_enable
.Pq Vt bool
If set to
@ -4786,6 +4793,7 @@ The default is 30.
.Xr sysctl 8 ,
.Xr syslogd 8 ,
.Xr timed 8 ,
.Xr unbound 8 ,
.Xr usbconfig 8 ,
.Xr wlandebug 8 ,
.Xr yp 8 ,

View File

@ -35,7 +35,7 @@
.\" @(#)rc.8 8.2 (Berkeley) 12/11/93
.\" $FreeBSD$
.\"
.Dd January 14, 2012
.Dd September 23, 2013
.Dt RC 8
.Os
.Sh NAME
@ -312,6 +312,9 @@ Defaults to displaying the process ID of the program (if running).
If the script starts a process (rather than performing a one-off
operation), wait for the command to exit.
Otherwise it is not necessary to support this argument.
.It Cm enabled
Return 0 if the service is enabled and 1 if it is not.
This command does not print anything.
.It Cm rcvar
Display which
.Xr rc.conf 5

View File

@ -29,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 14, 2012
.Dd September 23, 2012
.Dt RC.SUBR 8
.Os
.Sh NAME
@ -379,6 +379,9 @@ Perform a
then a
.Cm start .
Defaults to displaying the process ID of the program (if running).
.It Cm enabled
Return 0 if the service is enabled and 1 if it is not.
This command does not print anything.
.It Cm rcvar
Display which
.Xr rc.conf 5

View File

@ -4375,6 +4375,7 @@ OLD_FILES+=usr/share/man/man8/telnetd.8.gz
#.endif
.if ${MK_UNBOUND} == no
OLD_FILES+=etc/rc.d/local_unbound
OLD_FILES+=usr/lib/private/libunbound.a
OLD_FILES+=usr/lib/private/libunbound.so
OLD_LIBS+=usr/lib/private/libunbound.so.5
@ -4385,6 +4386,7 @@ OLD_FILES+=usr/lib32/private/libunbound.so
OLD_LIBS+=usr/lib32/private/libunbound.so.5
OLD_FILES+=usr/lib32/private/libunbound_p.a
.endif
OLD_FILES+=usr/sbin/local-unbound-setup
OLD_FILES+=usr/sbin/unbound
OLD_FILES+=usr/sbin/unbound-anchor
OLD_FILES+=usr/sbin/unbound-checkconf

View File

@ -1,5 +1,6 @@
# $FreeBSD$
SUBDIR= daemon anchor checkconf control
SUBDIR+= local-setup
.include <bsd.subdir.mk>

View File

@ -0,0 +1,6 @@
# $FreeBSD$
SCRIPTS= local-unbound-setup.sh
MAN= #
.include <bsd.prog.mk>

View File

@ -0,0 +1,357 @@
#!/bin/sh
#-
# 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$
#
#
# Configuration variables
#
user=""
unbound_conf=""
forward_conf=""
workdir=""
chrootdir=""
anchor=""
pidfile=""
resolv_conf=""
resolvconf_conf=""
service=""
start_unbound=""
forwarders=""
#
# Global variables
#
self=$(basename $(realpath "$0"))
bkext=$(date "+%Y%m%d.%H%M%S")
#
# Set default values for unset configuration variables.
#
set_defaults() {
: ${user:=unbound}
: ${workdir:=/var/unbound}
: ${unbound_conf:=${workdir}/unbound.conf}
: ${forward_conf:=${workdir}/forward.conf}
: ${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}
}
#
# 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}" "${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
}
#
# 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() {
echo "# Generated by $self"
echo "name_servers=\"127.0.0.1\""
echo "unbound_conf=\"${forward_conf}\""
echo "unbound_pid=\"${pidfile}\""
echo "unbound_service=\"${service}\""
# resolvconf(8) likes to restart rather than reload - consider
# forcing its hand?
#echo "unbound_restart=\"service ${service} reload\""
}
#
# Generate forward.conf
#
gen_forward_conf() {
echo "# Generated by $self"
echo "forward-zone:"
echo " name: ."
for forwarder ; do
if expr "${forwarder}" : "^[0-9:.]\{1,\}$" >/dev/null ; then
echo " forward-addr: ${forwarder}"
else
echo " forward-host: ${forwarder}"
fi
done
}
#
# Generate unbound.conf
#
gen_unbound_conf() {
echo "# Generated by $self"
echo "server:"
echo " username: ${user}"
echo " directory: ${workdir}"
echo " chroot: ${chrootdir}"
echo " pidfile: ${pidfile}"
echo " auto-trust-anchor-file: ${anchor}"
echo ""
if [ -f "${forward_conf}" ] ; then
echo "include: ${forward_conf}"
fi
}
#
# 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
echo "${file} created"
mv "${newfile}" "${file}"
elif ! cmp -s "${file}" "${newfile}" ; then
local oldfile="${file}.${bkext}"
echo "original ${file} saved as ${oldfile}"
mv "${file}" "${oldfile}"
mv "${newfile}" "${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 unbound configuration"
echo " -f path full path to forwarding 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:f:np:R:r:s:u:w:" option ; do
case $option in
a)
anchor="$OPTARG"
;;
c)
unbound_conf="$OPTARG"
;;
f)
forward_conf="$OPTARG"
;;
n)
start_unbound="no"
;;
p)
pidfile="$OPTARG"
;;
R)
resolvconf_conf="$OPTARG"
;;
r)
resolv_conf="$OPTARG"
;;
s)
service="$OPTARG"
;;
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="$@"
if [ -z "$forwarders" ] ; then
echo "Extracting forwarders from ${resolv_conf}."
forwarders=$(get_nameservers <"${resolv_conf}")
fi
#
# 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
else
local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX")
gen_forward_conf ${forwarders} >"${tmp_forward_conf}"
replace "${forward_conf}" "${tmp_forward_conf}"
fi
#
# Generate unbound.conf.
#
local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX")
set_chrootdir
gen_unbound_conf >"${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")
gen_resolvconf_conf >"${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}" >"${tmp_resolv_conf}"
replace "${resolv_conf}" "${tmp_resolv_conf}"
}
main "$@"