]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/unbound/setup/local-unbound-setup.sh
Update to Zstandard 1.3.8
[FreeBSD/FreeBSD.git] / usr.sbin / unbound / setup / local-unbound-setup.sh
1 #!/bin/sh
2 #-
3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 #
5 # Copyright (c) 2013 Dag-Erling Smørgrav
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 # 1. Redistributions of source code must retain the above copyright
12 #    notice, this list of conditions and the following disclaimer.
13 # 2. Redistributions in binary form must reproduce the above copyright
14 #    notice, this list of conditions and the following disclaimer in the
15 #    documentation and/or other materials provided with the distribution.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 # SUCH DAMAGE.
28 #
29 # $FreeBSD$
30 #
31
32 D="${DESTDIR}"
33 echo "destination: ${D}"
34
35 #
36 # Configuration variables
37 #
38 user=""
39 unbound_conf=""
40 forward_conf=""
41 lanzones_conf=""
42 control_conf=""
43 control_socket=""
44 workdir=""
45 confdir=""
46 chrootdir=""
47 anchor=""
48 pidfile=""
49 resolv_conf=""
50 resolvconf_conf=""
51 service=""
52 start_unbound=""
53 use_tls=""
54 forwarders=""
55
56 #
57 # Global variables
58 #
59 self=$(basename $(realpath "$0"))
60 bkdir=/var/backups
61 bkext=$(date "+%Y%m%d.%H%M%S")
62
63 #
64 # Regular expressions
65 #
66 RE_octet="([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
67 RE_ipv4="(${RE_octet}(\\.${RE_octet}){3})"
68 RE_word="([0-9A-Fa-f]{1,4})"
69 RE_ipv6="((${RE_word}:){1,}(:|(:${RE_word})*)|::1)"
70 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]))))"
71 RE_dnsname="([0-9A-Za-z-]{1,}(\\.[0-9A-Za-z-]{1,})*\\.?)"
72 RE_forward_addr="((${RE_ipv4}|${RE_ipv6})(@${RE_port})?)"
73 RE_forward_name="(${RE_dnsname}(@${RE_port})?)"
74 RE_forward_tls="(${RE_forward_addr}(#${RE_dnsname})?)"
75
76 #
77 # Set default values for unset configuration variables.
78 #
79 set_defaults() {
80         : ${user:=unbound}
81         : ${workdir:=/var/unbound}
82         : ${confdir:=${workdir}/conf.d}
83         : ${unbound_conf:=${workdir}/unbound.conf}
84         : ${forward_conf:=${workdir}/forward.conf}
85         : ${lanzones_conf:=${workdir}/lan-zones.conf}
86         : ${control_conf:=${workdir}/control.conf}
87         : ${control_socket:=/var/run/local_unbound.ctl}
88         : ${anchor:=${workdir}/root.key}
89         : ${pidfile:=/var/run/local_unbound.pid}
90         : ${resolv_conf:=/etc/resolv.conf}
91         : ${resolvconf_conf:=/etc/resolvconf.conf}
92         : ${service:=local_unbound}
93         : ${start_unbound:=yes}
94         : ${use_tls:=no}
95 }
96
97 #
98 # Verify that the configuration files are inside the working
99 # directory, and if so, set the chroot directory accordingly.
100 #
101 set_chrootdir() {
102         chrootdir="${workdir}"
103         for file in "${unbound_conf}" "${forward_conf}" \
104             "${lanzones_conf}" "${control_conf}" "${anchor}" ; do
105                 if [ "${file#${workdir%/}/}" = "${file}" ] ; then
106                         echo "warning: ${file} is outside ${workdir}" >&2
107                         chrootdir=""
108                 fi
109         done
110         if [ -z "${chrootdir}" ] ; then
111                 echo "warning: disabling chroot" >&2
112         fi
113 }
114
115 #
116 # Scan through /etc/resolv.conf looking for uncommented nameserver
117 # lines that don't point to localhost and return their values.
118 #
119 get_nameservers() {
120         while read line ; do
121                 local bareline=${line%%\#*}
122                 local key=${bareline%% *}
123                 local value=${bareline#* }
124                 case ${key} in
125                 nameserver)
126                         case ${value} in
127                         127.0.0.1|::1|localhost|localhost.*)
128                                 ;;
129                         *)
130                                 echo "${value}"
131                                 ;;
132                         esac
133                         ;;
134                 esac
135         done
136 }
137
138 #
139 # Scan through /etc/resolv.conf looking for uncommented nameserver
140 # lines.  Comment out any that don't point to localhost.  Finally,
141 # append a nameserver line that points to localhost, if there wasn't
142 # one already, and enable the edns0 option.
143 #
144 gen_resolv_conf() {
145         local localhost=no
146         local edns0=no
147         while read line ; do
148                 local bareline=${line%%\#*}
149                 local key=${bareline%% *}
150                 local value=${bareline#* }
151                 case ${key} in
152                 nameserver)
153                         case ${value} in
154                         127.0.0.1|::1|localhost|localhost.*)
155                                 localhost=yes
156                                 ;;
157                         *)
158                                 echo -n "# "
159                                 ;;
160                         esac
161                         ;;
162                 options)
163                         case ${value} in
164                         *edns0*)
165                                 edns0=yes
166                                 ;;
167                         esac
168                         ;;
169                 esac
170                 echo "${line}"
171         done
172         if [ "${localhost}" = "no" ] ; then
173                 echo "nameserver 127.0.0.1"
174         fi
175         if [ "${edns0}" = "no" ] ; then
176                 echo "options edns0"
177         fi
178 }
179
180 #
181 # Boilerplate
182 #
183 do_not_edit() {
184         echo "# This file was generated by $self."
185         echo "# Modifications will be overwritten."
186 }
187
188 #
189 # Generate resolvconf.conf so it updates forward.conf in addition to
190 # resolv.conf.  Note "in addition to" rather than "instead of",
191 # because we still want it to update the domain name and search path
192 # if they change.  Setting name_servers to "127.0.0.1" ensures that
193 # the libc resolver will try unbound first.
194 #
195 gen_resolvconf_conf() {
196         local style="$1"
197         do_not_edit
198         echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
199         if [ "${style}" = "dynamic" ] ; then
200                 echo "unbound_conf=\"${forward_conf}\""
201                 echo "unbound_pid=\"${pidfile}\""
202                 echo "unbound_service=\"${service}\""
203                 # resolvconf(8) likes to restart rather than reload
204                 echo "unbound_restart=\"service ${service} reload\""
205         else
206                 echo "# Static DNS configuration"
207         fi
208 }
209
210 #
211 # Generate forward.conf
212 #
213 gen_forward_conf() {
214         do_not_edit
215         echo "forward-zone:"
216         echo "        name: ."
217         for forwarder ; do echo "${forwarder}" ; done |
218         if [ "${use_tls}" = "yes" ] ; then
219                 echo "        forward-tls-upstream: yes"
220                 sed -nE \
221                     -e "s/^${RE_forward_tls}\$/        forward-addr: \\1/p"
222         else
223                 sed -nE \
224                     -e "s/^${RE_forward_addr}\$/        forward-addr: \\1/p" \
225                     -e "s/^${RE_forward_name}\$/        forward-host: \\1/p"
226         fi
227 }
228
229 #
230 # Generate lan-zones.conf
231 #
232 gen_lanzones_conf() {
233         do_not_edit
234         echo "server:"
235         echo "        # Unblock reverse lookups for LAN addresses"
236         echo "        unblock-lan-zones: yes"
237         echo "        insecure-lan-zones: yes"
238 }
239
240 #
241 # Generate control.conf
242 #
243 gen_control_conf() {
244         do_not_edit
245         echo "remote-control:"
246         echo "        control-enable: yes"
247         echo "        control-interface: ${control_socket}"
248         echo "        control-use-cert: no"
249 }
250
251 #
252 # Generate unbound.conf
253 #
254 gen_unbound_conf() {
255         do_not_edit
256         echo "server:"
257         echo "        username: ${user}"
258         echo "        directory: ${workdir}"
259         echo "        chroot: ${chrootdir}"
260         echo "        pidfile: ${pidfile}"
261         echo "        auto-trust-anchor-file: ${anchor}"
262         if [ "${use_tls}" = "yes" ] ; then
263                 echo "        tls-cert-bundle: /etc/ssl/cert.pem"
264         fi
265         echo ""
266         if [ -f "${forward_conf}" ] ; then
267                 echo "include: ${forward_conf}"
268         fi
269         if [ -f "${lanzones_conf}" ] ; then
270                 echo "include: ${lanzones_conf}"
271         fi
272         if [ -f "${control_conf}" ] ; then
273                 echo "include: ${control_conf}"
274         fi
275         if [ -d "${confdir}" ] ; then
276                 echo "include: ${confdir}/*.conf"
277         fi
278 }
279
280 #
281 # Rename a file we are about to replace.
282 #
283 backup() {
284         local file="$1"
285         if [ -f "${D}${file}" ] ; then
286                 local bkfile="${bkdir}/${file##*/}.${bkext}"
287                 echo "Original ${file} saved as ${bkfile}"
288                 mv "${D}${file}" "${D}${bkfile}"
289         fi
290 }
291
292 #
293 # Wrapper for mktemp which respects DESTDIR
294 #
295 tmp() {
296         local file="$1"
297         mktemp -u "${D}${file}.XXXXX"
298 }
299
300 #
301 # Replace one file with another, making a backup copy of the first,
302 # but only if the new file is different from the old.
303 #
304 replace() {
305         local file="$1"
306         local newfile="$2"
307         if [ ! -f "${D}${file}" ] ; then
308                 echo "${file} created"
309                 mv "${newfile}" "${D}${file}"
310         elif ! cmp -s "${D}${file}" "${newfile}" ; then
311                 backup "${file}"
312                 mv "${newfile}" "${D}${file}"
313         else
314                 echo "${file} not modified"
315                 rm "${newfile}"
316         fi
317 }
318
319 #
320 # Print usage message and exit
321 #
322 usage() {
323         exec >&2
324         echo "usage: $self [options] [forwarder ...]"
325         echo "options:"
326         echo "    -n          do not start unbound"
327         echo "    -a path     full path to trust anchor file"
328         echo "    -C path     full path to additional configuration directory"
329         echo "    -c path     full path to unbound configuration file"
330         echo "    -f path     full path to forwarding configuration"
331         echo "    -O path     full path to remote control socket"
332         echo "    -o path     full path to remote control configuration"
333         echo "    -p path     full path to pid file"
334         echo "    -R path     full path to resolvconf.conf"
335         echo "    -r path     full path to resolv.conf"
336         echo "    -s service  name of unbound service"
337         echo "    -u user     user to run unbound as"
338         echo "    -w path     full path to working directory"
339         exit 1
340 }
341
342 #
343 # Main
344 #
345 main() {
346         umask 022
347
348         #
349         # Parse and validate command-line options
350         #
351         while getopts "a:C:c:f:no:p:R:r:s:tu:w:" option ; do
352                 case $option in
353                 a)
354                         anchor="$OPTARG"
355                         ;;
356                 C)
357                         confdir="$OPTARG"
358                         ;;
359                 c)
360                         unbound_conf="$OPTARG"
361                         ;;
362                 f)
363                         forward_conf="$OPTARG"
364                         ;;
365                 n)
366                         start_unbound="no"
367                         ;;
368                 O)
369                         control_socket="$OPTARG"
370                         ;;
371                 o)
372                         control_conf="$OPTARG"
373                         ;;      
374                 p)
375                         pidfile="$OPTARG"
376                         ;;
377                 R)
378                         resolvconf_conf="$OPTARG"
379                         ;;
380                 r)
381                         resolv_conf="$OPTARG"
382                         ;;
383                 s)
384                         service="$OPTARG"
385                         ;;
386                 t)
387                         use_tls="yes"
388                         ;;
389                 u)
390                         user="$OPTARG"
391                         ;;
392                 w)
393                         workdir="$OPTARG"
394                         ;;
395                 *)
396                         usage
397                         ;;
398                 esac
399         done
400         shift $((OPTIND-1))
401         set_defaults
402
403         #
404         # Get the list of forwarders, either from the command line or
405         # from resolv.conf.
406         #
407         forwarders="$@"
408         case "${forwarders}" in
409         [Nn][Oo][Nn][Ee])
410                 forwarders="none"
411                 style=recursing
412                 ;;
413         "")
414                 if [ -f "${D}${resolv_conf}" ] ; then
415                         echo "Extracting forwarders from ${resolv_conf}."
416                         forwarders=$(get_nameservers <"${D}${resolv_conf}")
417                 fi
418                 style=dynamic
419                 ;;
420         *)
421                 style=static
422                 ;;
423         esac
424
425         #
426         # Generate forward.conf.
427         #
428         if [ -z "${forwarders}" ] ; then
429                 echo -n "No forwarders found in ${resolv_conf##*/}, "
430                 if [ -f "${forward_conf}" ] ; then
431                         echo "using existing ${forward_conf##*/}."
432                 else
433                         echo "unbound will recurse."
434                 fi
435         elif [ "${forwarders}" = "none" ] ; then
436                 echo "Forwarding disabled, unbound will recurse."
437                 backup "${forward_conf}"
438         else
439                 local tmp_forward_conf=$(tmp "${forward_conf}")
440                 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}"
441                 replace "${forward_conf}" "${tmp_forward_conf}"
442         fi
443
444         #
445         # Generate lan-zones.conf.
446         #
447         local tmp_lanzones_conf=$(tmp "${lanzones_conf}")
448         gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}"
449         replace "${lanzones_conf}" "${tmp_lanzones_conf}"
450
451         #
452         # Generate control.conf.
453         #
454         local tmp_control_conf=$(tmp "${control_conf}")
455         gen_control_conf | unexpand >"${tmp_control_conf}"
456         replace "${control_conf}" "${tmp_control_conf}"
457
458         #
459         # Generate unbound.conf.
460         #
461         local tmp_unbound_conf=$(tmp "${unbound_conf}")
462         set_chrootdir
463         gen_unbound_conf | unexpand >"${tmp_unbound_conf}"
464         replace "${unbound_conf}" "${tmp_unbound_conf}"
465
466         #
467         # Start unbound, unless requested not to.  Stop immediately if
468         # it is not enabled so we don't end up with a resolv.conf that
469         # points into nothingness.  We could "onestart" it, but it
470         # wouldn't stick.
471         #
472         if [ "${start_unbound}" = "no" ] ; then
473                 # skip
474         elif ! service "${service}" enabled ; then
475                 echo "Please enable $service in rc.conf(5) and try again."
476                 return 1
477         elif ! service "${service}" restart ; then
478                 echo "Failed to start $service."
479                 return 1
480         fi
481
482         #
483         # Rewrite resolvconf.conf so resolvconf updates forward.conf
484         # instead of resolv.conf.
485         #
486         local tmp_resolvconf_conf=$(tmp "${resolvconf_conf}")
487         gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}"
488         replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
489
490         #
491         # Finally, rewrite resolv.conf.
492         #
493         local tmp_resolv_conf=$(tmp "${resolv_conf}")
494         gen_resolv_conf <"${D}${resolv_conf}" | unexpand >"${tmp_resolv_conf}"
495         replace "${resolv_conf}" "${tmp_resolv_conf}"
496 }
497
498 main "$@"